# Reverse Not URL Encoding Apostrophe with %27

**URL:** <https://forum.djangoproject.com/t/reverse-not-url-encoding-apostrophe-with-27/40356>\
**Category:** Getting Started\
**Created:** [April 11, 2025, 11:58am UTC](https://forum.djangoproject.com/t/reverse-not-url-encoding-apostrophe-with-27/40356 "2025-04-11T11:58:31Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![JonnyWaffles](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/jonnywaffles/32/27651_2.png) [@JonnyWaffles](https://forum.djangoproject.com/u/JonnyWaffles)\
**Post date:** [April 11, 2025, 11:58am UTC](https://forum.djangoproject.com/t/reverse-not-url-encoding-apostrophe-with-27/40356/1 "2025-04-11T11:58:32Z")

</div>

Hi friends,  
For _reasons_ I need to use `reverse` a url parameter string value containing an apostrophe. Reverse does not url encode the apostrophe, but Python’s standard library url `quote` does.  
See this test

```python
>>> reverse("demo", kwargs={"workstep": "Director's Assistant"})
"/whatever/Director's%20Assistant/"
>>> quote("Director's Assistant")
'Director%27s%20Assistant'

```

You can see `reverse` does not encode the apostrophe, but `quote` leverages `%27` as expected. Would you be so kind as to help me understand why reverse and quote are getting different results? I suspect I am missing something obvious.

Surprisingly, the built-in Django runserver resolves values with the unencoded apostrophe, but I do worry other servers would complain. Further, I clearly don’t understand why reverse and quote would result in different encoded values!

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [April 11, 2025, 12:21pm UTC](https://forum.djangoproject.com/t/reverse-not-url-encoding-apostrophe-with-27/40356/2 "2025-04-11T12:21:05Z")

</div>

Whenever there’s a question concerning the encoding of a standardized element, the applicable RFCs are always the first place to check.

In this case, the guiding RFC would be [RFC3986, section 2.2. Reserved Characters](https://datatracker.ietf.org/doc/html/rfc3986#section-2.2).

The apostrophe is identified in the list of `sub-delims`, which are identified as being valid characters within a url, in the same generic category as other characters like `&`, `;`, `=`, `!`, and a couple others, making it a valid character within a url.

---

<div class="post-metadata">

**Author:** ![JonnyWaffles](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/jonnywaffles/32/27651_2.png) [@JonnyWaffles](https://forum.djangoproject.com/u/JonnyWaffles)\
**Post date:** [April 11, 2025, 12:26pm UTC](https://forum.djangoproject.com/t/reverse-not-url-encoding-apostrophe-with-27/40356/3 "2025-04-11T12:26:31Z")

</div>

Interesting. Thanks Ken, do you know offhand why urllib quote escapes it then? Is just a matter of context? Perhaps urllib’s quote assuming the value is not being used as a sub-delim?

Quoting urllib

> By default, this function is intended for quoting the path section of a URL.

So I suspect the “right” way to pass the value into the path parameter is urllib’s, whereas Django is respecting the apostrophe could be a sub-delim. I think the best solution for my scenario is to leverage quote since the value is used as a path param.

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [April 11, 2025, 12:34pm UTC](https://forum.djangoproject.com/t/reverse-not-url-encoding-apostrophe-with-27/40356/4 "2025-04-11T12:34:23Z")

</div>

Quoting directly from [urllib.parse — Parse URLs into components — Python 3.13.3 documentation](https://docs.python.org/3/library/urllib.parse.html#urllib.parse.quote:)

> Replace special characters in _string_ using the `%xx` escape. Letters, digits, and the characters `'_.-~'` are never quoted. By default, this function is intended for quoting the path section of a URL. The optional _safe_ parameter specifies additional ASCII characters that should not be quoted — its default value is `'/'` .

While I’m not an authoritative source on why anything is being done, my guess is that they took this approach as a conservative approach. What I mean is that I would assume their implementation makes it easier to add characters to the safe list than it would be to remove them. In that situation, then the safe list would represent the smallest set of safe characters, to be extended on an as-needed basis.

> [@JonnyWaffles](#):
>
> I think the best solution for my scenario is to leverage quote since the value is used as a path param.

`<conjecture>`  
I’m not sure it matters - any standard-compliant web server should pass that url through directly, and assuming your `path` definition for the url accepts it as part of the path, it should work directly.  
`</conjecture>`

---

<div class="post-metadata">

**Author:** ![JonnyWaffles](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/jonnywaffles/32/27651_2.png) [@JonnyWaffles](https://forum.djangoproject.com/u/JonnyWaffles)\
**Post date:** [April 11, 2025, 12:44pm UTC](https://forum.djangoproject.com/t/reverse-not-url-encoding-apostrophe-with-27/40356/5 "2025-04-11T12:44:32Z")

</div>

The plot thickens, when Django renders my reverse + quote version as `/Director%27s%20Assistant`, my JS library using the value sends the request as `/Director's%20Assistant/` so it appears leaving the apostrophe in place is fine. Thanks for all your help Ken.
