diff --git a/OliverBooth/Pages/Blog/RawArticle.cshtml b/OliverBooth/Pages/Blog/RawArticle.cshtml
new file mode 100644
index 0000000..01625ef
--- /dev/null
+++ b/OliverBooth/Pages/Blog/RawArticle.cshtml
@@ -0,0 +1,2 @@
+@page "/blog/{year:int}/{month:int}/{day:int}/{slug}/raw"
+@model OliverBooth.Pages.Blog.RawArticle
\ No newline at end of file
diff --git a/OliverBooth/Pages/Blog/RawArticle.cshtml.cs b/OliverBooth/Pages/Blog/RawArticle.cshtml.cs
new file mode 100644
index 0000000..3f12fa5
--- /dev/null
+++ b/OliverBooth/Pages/Blog/RawArticle.cshtml.cs
@@ -0,0 +1,52 @@
+using Cysharp.Text;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using OliverBooth.Data.Blog;
+using OliverBooth.Services;
+
+namespace OliverBooth.Pages.Blog;
+
+///
+/// Represents the page model for the RawArticle page.
+///
+public class RawArticle : PageModel
+{
+ private readonly BlogService _blogService;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The .
+ public RawArticle(BlogService blogService)
+ {
+ _blogService = blogService;
+ }
+
+ ///
+ /// Gets the requested blog post.
+ ///
+ /// The requested blog post.
+ public BlogPost Post { get; private set; } = null!;
+
+ public IActionResult OnGet(int year, int month, int day, string slug)
+ {
+ if (!_blogService.TryGetBlogPost(year, month, day, slug, out BlogPost? post))
+ {
+ Response.StatusCode = 404;
+ return NotFound();
+ }
+
+ using Utf8ValueStringBuilder builder = ZString.CreateUtf8StringBuilder();
+ builder.AppendLine("# " + post.Title);
+ if (_blogService.TryGetAuthor(post, out Author? author))
+ builder.AppendLine($"Author: {author.Name}");
+
+ builder.AppendLine($"Published: {post.Published:R}");
+ if (post.Updated.HasValue)
+ builder.AppendLine($"Updated: {post.Updated:R}");
+
+ builder.AppendLine();
+ builder.AppendLine(post.Body);
+ return Content(builder.ToString(), "text/plain");
+ }
+}