All posts

How to Escape Special Characters in Markdown

The markdown escape character is a backslash. What it works on, why it does nothing inside code spans, and how to escape pipes in tables.

Short answer: The markdown escape character is a backslash. Put a \ before any punctuation mark you want treated as literal text rather than syntax — \*not italic\*, \# not a heading, \[not a link\]. Inside code spans and code blocks the backslash does nothing, because backticks already make everything literal.

Every markdown symbol is a normal character that happens to be load-bearing. An asterisk is usually an asterisk, until it's emphasis. A hash is usually a hash, until it's a heading. When you need the character and the parser thinks you meant the syntax, you escape it.


The Backslash

Put a backslash immediately before the character:

\*This is not italic\*
\# This is not a heading
\[This is not a link\]
5 \* 3 = 15

Renders as:

*This is not italic*
# This is not a heading
[This is not a link]
5 * 3 = 15

The backslash itself disappears from the output.


What Can Be Escaped

The original 2004 markdown spec listed a specific set of escapable characters:

\   backslash          `   backtick
*   asterisk           _   underscore
{ } curly braces       [ ] square brackets
( ) parentheses        #   hash
+   plus               -   hyphen
.   period             !   exclamation mark

GitHub Flavored Markdown adds the pipe | for tables.

CommonMark widened this considerably: any ASCII punctuation character can be backslash-escaped, whether or not it means anything in markdown. That's the 32 characters:

!  "  #  $  %  &  '  (  )  *  +  ,  -  .  /  :  ;  <  =
>  ?  @  [  \  ]  ^  _  `  {  |  }  ~

Escaping a character with no special meaning is harmless — \@ just produces @. The exception that matters: a backslash before a non-punctuation character is a literal backslash. \d in a regex stays \d. That's why you can paste most regular expressions into markdown prose without them being mangled, as long as they don't contain asterisks or underscores.

Backslash at the end of a line is the one escape that isn't literal: in CommonMark it's a hard line break, equivalent to two trailing spaces but visible in the source.


Escaping Doesn't Work Inside Code

This surprises people. Inside a code span or a fenced code block, backslashes are literal characters, not escapes:

`\*literal asterisks\*`

Renders as \*literal asterisks\* — backslashes included. That's correct: the whole point of a code span is that its contents are taken literally, so nothing needs escaping and nothing gets stripped.

The consequence is that you never escape inside code. If you want to show markdown syntax to a reader, wrap it in backticks and leave it alone. If you're escaping inside a code fence, you're producing wrong output.


Escaping Backticks: Use More Backticks

A backslash before a backtick works in prose, but the idiomatic approach is to use a longer fence than the content:

To show one backtick, wrap in two: `` ` ``
To show two backticks, wrap in three: ``` `` ```

The rule: the delimiter must be longer than any run of backticks inside it, and you add a space after the opening delimiter and before the closing one if the content itself starts or ends with a backtick. Markdown strips exactly one leading and one trailing space when the code span both starts and ends with one.

Same principle for fenced blocks. To show a fenced code block inside a fenced code block, use four backticks on the outside:

```bash
echo "hello"
```

Escaping Pipes in Tables

This is the escape that breaks the most documents. In a GFM table, the pipe character delimits cells. A literal pipe inside a cell splits it into two:

| Operator | Meaning |
|----------|---------|
| \|\|     | Logical OR |
| \|       | Bitwise OR |

Without the backslashes, the row shatters and the table misaligns from that row down.

The part that catches everyone: you must escape pipes inside code spans in a table too. GFM splits the row into cells before it parses inline content, so backticks don't protect a pipe the way they protect an asterisk:

| Command | Description |
|---------|-------------|
| `ps aux \| grep node` | Find node processes |

The escaped pipe still renders as a plain | inside the code span. It looks wrong in the source and it is correct in the output. This is a documented GFM quirk, not a bug in your renderer.

If a table cell needs several pipes and starts becoming unreadable, use the HTML entity &#124; instead — it survives the cell split cleanly. Our markdown tables syntax guide covers alignment, multi-line cells, and the rest of the table edge cases.


When You Don't Need to Escape

Over-escaping makes source unreadable. Several cases look like they need a backslash and don't.

Underscores inside words. CommonMark and GFM don't treat intraword underscores as emphasis, specifically so that identifiers survive:

snake_case_variable_name
MAX_BUFFER_SIZE
__init__

All render literally, no escaping needed. Asterisks are different — intra*word*asterisks does produce emphasis, so those need escaping.

Hashes not at the start of a line. Issue #42 is fine. Only a hash in the first column makes a heading.

Periods after a number mid-sentence. Only 1. at the start of a line starts an ordered list.

Ampersands and angle brackets. Most parsers handle & and < sensibly in prose. AT&T renders correctly. But <something> that looks like an HTML tag will be treated as one, so escape those: \<tag\> or use &lt;tag&gt;.


HTML Entities as an Alternative

Anywhere a backslash is awkward, a character entity does the same job:

&#42;   asterisk         &lowbar;  underscore
&#124;  pipe             &num;     hash
&lt;    less than        &gt;      greater than
&amp;   ampersand        &#96;     backtick

Entities are more verbose but they're immune to the escaping rules — they work in table cells, in HTML blocks, and in renderers with unusual backslash handling. They do not work inside code spans, where they render literally as &#42;.


Escaping in URLs

Link destinations follow URL rules, not markdown rules. Backslash escaping mostly doesn't apply.

Spaces: percent-encode as %20, or wrap the destination in angle brackets:

[Report](<my report final.pdf>)
[Report](my%20report%20final.pdf)

Parentheses: a closing paren inside a URL ends the link early. Escape it with a backslash or percent-encode it as %29:

[Wikipedia](https://en.wikipedia.org/wiki/Markdown_\(disambiguation\))

Everything else: percent-encode. # becomes %23 unless it's a real fragment identifier, ? and & keep their query-string meaning, and non-ASCII characters should be percent-encoded for maximum compatibility even though most modern renderers handle them.


Seeing What You Actually Wrote

Escaping problems are hard to spot in raw source, because the failure mode is usually a missing character rather than an error. A side-by-side view is the fastest diagnostic: OpenMark shows syntax-colored source in Markdown view and the rendered result in Document view, so an unescaped pipe that collapsed a table row is visible immediately rather than after you push.

For the rest of the syntax, see the markdown cheat sheet. Two related places where a stray character changes meaning: markdown horizontal lines and dividers, where three hyphens can become a heading, and markdown strikethrough, where a single tilde means different things in different parsers.


Download OpenMark → — $9.99, one-time, native macOS. Syntax coloring shows you what the parser sees, before an unescaped character breaks your table.