BUG: to_html with render_links=True and escape=True does not escape the href attribute
Author: Str1ckl4ndCreated Jun 28, 2026Updated Sep 17, 2026
LabelsBugIO HTML
### Pandas version checks
- [x] I have checked that this issue has not already been reported.
- [x] I have confirmed this bug exists on the [latest version](https://pandas.pydata.org/docs/whatsnew/index.html) of pandas.
- [x] I have confirmed this bug exists on the [main branch](https://pandas.pydata.org/docs/dev/getting_started/install.html#installing-the-development-version-of-pandas) of pandas.
### Reproducible Example
```python
import pandas as pd
value = 'http://example.com/search?q=a"b&lang=en'
html = pd.DataFrame({"url": [value]}).to_html(render_links=True, escape=True)
print(html)
assert "http://example.com/search?q=a"b&lang=en" in html
assert 'href="http://example.com/search?q=a"b&lang=en"' in html
```
### Issue Description
`DataFrame.to_html(render_links=True, escape=True)` escapes the visible cell text, but the generated `href` attribute uses the original cell value without the same HTML escaping.
The relevant implementation is in `pandas/io/formats/html.py`:
```python
rs = pprint_thing(s, escape_chars=esc).strip()
if self.render_links and is_url(rs):
rs_unescaped = pprint_thing(s, escape_chars={}).strip()
start_tag += f''
```
With the value from the example above, the visible cell text is escaped as expected, but the `href` attribute keeps the raw double quote and ampersand. The generated table therefore has different escaping behavior between the visible URL text and the generated link URL.
Current generated output includes this shape:
```html
http://example.com/search?q=a"b&lang=en
```
This is surprising because `escape=True` changes the visible text to `"` and `&`, while `render_links=True` keeps the raw characters in the generated `href`.
### Expected Behavior
When `escape=True` and `render_links=True` are both enabled, the generated anchor should apply consistent HTML escaping to the URL value before placing it in `href`.
For example, the generated output should represent the same URL value consistently in both the anchor's `href` attribute and the visible link text.
### Installed Versions
Directly visible environment information:
- pandas: 3.0.3
- Python container image: python:3.13-slim
Reproduced with pandas `3.0.3`. The same `rs_unescaped` implementation is also present on the current `main` branch at `pandas/io/formats/html.py`.
Source: pandas-dev/pandas