diff --git a/pygitweb/main.py b/pygitweb/main.py
index 25bb7a8..78aba52 100644
--- a/pygitweb/main.py
+++ b/pygitweb/main.py
@@ -533,6 +533,23 @@ def _object_type(project: str, ref: str) -> str | None:
     return git_get_type(project, ref)
 
 
+# Common README filenames to look for at repo root (order matters: prefer README.md)
+README_CANDIDATES = ["README.md", "README", "readme.md", "readme", "Readme.md"]
+
+
+def _get_readme_at_head(project: str, head: str | None) -> tuple[str, str] | None:
+    """If a README exists at HEAD, return (filename, utf8_content). Otherwise None."""
+    if not head:
+        return None
+    for name in README_CANDIDATES:
+        result = get_blob_at_ref_path(project, head, name)
+        if result:
+            blob, _ = result
+            text = to_utf8(blob.data) or ""
+            return (name, text)
+    return None
+
+
 def git_summary(project: str) -> HTMLResponse:
     """Project summary page. Port of git_summary."""
     descr = git_get_project_description(project) or "none"
@@ -552,7 +569,33 @@ def git_summary(project: str) -> HTMLResponse:
         ]
     )
     pre = PREAMBLE.render(title=f"{esc_html(config.SITE_NAME)} - {project}", theme="dark", site_name=config.SITE_NAME)
-    return HTMLResponse(f"{pre}<h1>{esc_html(project)}</h1>{table}{POSTAMBLE}")
+    body_parts = [f"{pre}<h1>{esc_html(project)}</h1>{table}"]
+
+    readme = _get_readme_at_head(project, head)
+    if readme:
+        readme_filename, readme_content = readme
+        is_markdown = readme_filename.lower().endswith(".md")
+        # Container below repo info table
+        if is_markdown:
+            readme_b64 = base64.b64encode(readme_content.encode("utf-8")).decode("ascii")
+            blob_base = f"/{project}?a=blob&h={quote(head or '', safe='')}&f="
+            blob_base_attr = blob_base.replace("&", "&amp;").replace('"', "&quot;")
+            body_parts.append(
+                '<div class="card mt-3"><div class="card-header"><h2 class="card-title">README</h2></div>'
+                '<div class="card-body"><div id="readme-container" class="markdown-body" data-readme-b64="'
+                + readme_b64 + '" data-blob-base="' + blob_base_attr + '"></div></div></div>'
+            )
+            body_parts.append('<script src="/static/readme-render.js"></script>')
+        else:
+            body_parts.append(
+                '<div class="card mt-3"><div class="card-header"><h2 class="card-title">README</h2></div>'
+                '<div class="card-body"><pre class="readme-plain"><code>'
+                + esc_html(sanitize(readme_content) or "")
+                + "</code></pre></div></div>"
+            )
+
+    body_parts.append(POSTAMBLE)
+    return HTMLResponse("".join(body_parts))
 
 
 def git_remotes(project: str) -> HTMLResponse:
diff --git a/pygitweb/static/readme-render.js b/pygitweb/static/readme-render.js
new file mode 100644
index 0000000..ab7d3dc
--- /dev/null
+++ b/pygitweb/static/readme-render.js
@@ -0,0 +1,38 @@
+(function() {
+  var el = document.getElementById("Showdown-script");
+  function isRelativeLink(href) {
+    if (!href || href.charAt(0) === "#") return false;
+    if (href.charAt(0) === "/") return false;
+    if (/^[a-z][a-z0-9+.-]*:/i.test(href)) return false;
+    if (href.indexOf("//") === 0) return false;
+    return true;
+  }
+  function rewriteRelativeLinks(container, blobBase) {
+    if (!blobBase) return;
+    var links = container.querySelectorAll("a[href]");
+    for (var i = 0; i < links.length; i++) {
+      var a = links[i];
+      var href = a.getAttribute("href");
+      if (href && isRelativeLink(href)) {
+        var path = href.replace(/^\.\//, "");
+        a.setAttribute("href", blobBase + encodeURIComponent(path));
+      }
+    }
+  }
+  function render() {
+    var container = document.getElementById("readme-container");
+    var enc = container && container.getAttribute("data-readme-b64");
+    var blobBase = container && container.getAttribute("data-blob-base");
+    if (!container || !enc || typeof showdown === "undefined") return;
+    try {
+      var raw = atob(enc);
+      var conv = new showdown.Converter();
+      container.innerHTML = conv.makeHtml(raw);
+      rewriteRelativeLinks(container, blobBase);
+    } catch (e) {}
+  }
+  if (el) {
+    if (el.getAttribute("data-loaded")) render();
+    else el.addEventListener("load", function() { el.setAttribute("data-loaded", "1"); render(); });
+  }
+})();
diff --git a/pygitweb/templates/preamble.html b/pygitweb/templates/preamble.html
index 1e6c1fa..6c9a536 100644
--- a/pygitweb/templates/preamble.html
+++ b/pygitweb/templates/preamble.html
@@ -5,6 +5,7 @@
 <script id="MathJax-script" async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.2.2/es5/tex-chtml.min.js"></script>
 <script id="GraphViz-script" async src="https://cdnjs.cloudflare.com/ajax/libs/viz.js/1.8.2/viz.js"></script>
 <script id="diff2html-script" async src="https://cdn.jsdelivr.net/npm/diff2html/bundles/js/diff2html-ui.min.js"></script>
+<script id="Showdown-script" async src="https://cdnjs.cloudflare.com/ajax/libs/showdown/2.1.0/showdown.js"></script>
 <link href=" https://cdn.jsdelivr.net/npm/diff2html@3.4.56/bundles/css/diff2html.min.css " rel="stylesheet">
 <link rel="preconnect" href="https://fonts.googleapis.com">
 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
