diff --git a/par/md.test.ts b/par/md.test.ts
index 57af2b9..0325f83 100644
--- a/par/md.test.ts
+++ b/par/md.test.ts
@@ -5,7 +5,7 @@ import { MarkdownParser } from "./md.ts";
 const ren = new HtmlStrRenderer();
 
 Deno.test({
-  name: "should parse header",
+  name: "should parse empty ATX header",
   fn: () => {
     const par = new MarkdownParser();
     const res = par.parse("#");
@@ -14,16 +14,16 @@ Deno.test({
 });
 
 Deno.test({
-  name: "should parse header with text",
+  name: "should parse ATX header with text",
   fn: () => {
     const par = new MarkdownParser();
-    const res = par.parse("# hello");
-    assertEquals(ren.render(res), "<h1>hello</h1>");
+    assertEquals(ren.render(par.parse("# hello")), "<h1>hello</h1>");
+    assertEquals(ren.render(par.parse("# hello#")), "<h1>hello#</h1>");
   },
 });
 
 Deno.test({
-  name: "should parse header with specific level",
+  name: "should parse ATX header with specific level",
   fn: () => {
     const par = new MarkdownParser();
     assertEquals(ren.render(par.parse("# hello")), "<h1>hello</h1>");
@@ -36,7 +36,7 @@ Deno.test({
 });
 
 Deno.test({
-  name: "should parse header if line contains additional spaces",
+  name: "should parse ATX header if line contains additional spaces",
   fn: () => {
     const par = new MarkdownParser();
     assertEquals(ren.render(par.parse(" # hello")), "<h1>hello</h1>");
@@ -44,3 +44,15 @@ Deno.test({
     assertEquals(ren.render(par.parse("   # hello")), "<h1>hello</h1>");
   },
 });
+
+Deno.test({
+  name: "should parse ATX header with closing sequence",
+  fn: () => {
+    const par = new MarkdownParser();
+    assertEquals(ren.render(par.parse("# #")), "<h1></h1>");
+    assertEquals(ren.render(par.parse("# hello #")), "<h1>hello</h1>");
+    assertEquals(ren.render(par.parse("# hello #########")), "<h1>hello</h1>");
+    assertEquals(ren.render(par.parse("# hello #        ")), "<h1>hello</h1>");
+    assertEquals(ren.render(par.parse("###### hello #")), "<h6>hello</h6>");
+  },
+});
diff --git a/par/md.ts b/par/md.ts
index e6a275a..b41bcd3 100644
--- a/par/md.ts
+++ b/par/md.ts
@@ -2,7 +2,8 @@ import { AnyNode, Elem, Fragment, TextNode } from "../core/node.ts";
 import { isNil } from "../core/utils.ts";
 import { Parser } from "./types.ts";
 
-const RE_OPEN_HEADING = /^\s{0,3}(#{1,6})(\s|$)/;
+const RE_OPEN_ATX_HEADING = /^\s{0,3}(#{1,6})(\s|$)/;
+const RE_CLOSE_ATX_HEADING = /(^|\s+)#*\s*$/;
 
 export class MarkdownParser implements Parser {
   parse(input: string): AnyNode {
@@ -10,29 +11,32 @@ export class MarkdownParser implements Parser {
 
     let readStr = input;
 
-    const match = RE_OPEN_HEADING.exec(readStr);
+    const match = RE_OPEN_ATX_HEADING.exec(readStr);
     if (!isNil(match)) {
       readStr = readStr.slice(match[0].length);
 
-      console.log({ match });
-
-      const heading: AstHeading = {
-        kind: AstKind.Heading,
+      const atxHeading: AstAtxHeading = {
+        kind: AstKind.AtxHeading,
         level: match[1].length as HeadingLevel,
         content: [],
       };
-      ast.content.push(heading);
+      ast.content.push(atxHeading);
 
       if (match[2].length > 0) {
-        const textContent = readStr.split("\n", 1)[0];
-        readStr = readStr.slice(textContent.length);
+        const endMatch = RE_CLOSE_ATX_HEADING.exec(readStr);
 
-        const text: AstText = {
-          kind: AstKind.Text,
-          content: textContent,
-        };
+        const headingContent = isNil(endMatch)
+          ? readStr.split("\n", 1)[0]
+          : readStr.slice(0, endMatch.index);
+        readStr = readStr.slice(headingContent.length);
 
-        heading.content.push(text);
+        if (headingContent.trim().length) {
+          const text: AstText = {
+            kind: AstKind.Text,
+            content: headingContent.trim(),
+          };
+          atxHeading.content.push(text);
+        }
       }
     }
 
@@ -40,7 +44,7 @@ export class MarkdownParser implements Parser {
   }
 }
 
-function Heading(ast: AstHeading): Elem {
+function Heading(ast: AstAtxHeading): Elem {
   return new Elem(`h${ast.level}`, {}, ast.content.map(Text));
 }
 
@@ -51,9 +55,9 @@ function Text(ast: AstText): TextNode {
 // AST
 
 type AstDocument = BaseAstItem<AstKind.Document, AstDocumentChild[]>;
-type AstDocumentChild = AstHeading;
+type AstDocumentChild = AstAtxHeading;
 
-interface AstHeading extends BaseAstItem<AstKind.Heading, AstText[]> {
+interface AstAtxHeading extends BaseAstItem<AstKind.AtxHeading, AstText[]> {
   level: HeadingLevel;
 }
 
@@ -68,6 +72,6 @@ interface BaseAstItem<K extends AstKind, Cont> {
 
 enum AstKind {
   Document,
-  Heading,
+  AtxHeading,
   Text,
 }