---
url: https://docs.w3id.org/rules/htaccess/escape-literal-dots.md
---

# `htaccess/escape-literal-dots`

**Severity:** warning · **Status:** enforced · **Applies to:** `ids/**/.htaccess`

## What

A `.` in a `RewriteRule` pattern matches *any single character*. To match a
literal dot — in a filename, a version number, or a media type — escape it as
`\.`.

## Why

`^vocab.ttl$` looks like it matches `vocab.ttl`, and it does. It also matches
`vocabXttl`, `vocab-ttl`, `vocab1ttl`, and every other single character in that
position.

That is rarely what you want, and it produces redirects for URLs you never
intended to serve — which then fail at the destination, giving the user a 404
from your server instead of a clean 404 from w3id.org. In a versioned scheme it
can silently route one version's requests to another.

The same applies inside `RewriteCond` patterns, where media types are the usual
victim: `application/ld+json` is a regular expression in which `+` means "one or
more of the preceding character".

## Wrong

```apache
RewriteRule ^vocab.ttl$ https://example.org/vocab.ttl [R=303,L]
RewriteRule ^(1.0.0)$ https://example.org/$1/vocab.ttl [R=303,L]

RewriteCond %{HTTP_ACCEPT} application/ld+json
```

## Right

```apache
RewriteRule ^vocab\.ttl$ https://example.org/vocab.ttl [R=303,L]
RewriteRule ^(1\.0\.0)$ https://example.org/$1/vocab.ttl [R=303,L]

RewriteCond %{HTTP_ACCEPT} application/ld\+json
```

A version pattern in general:

```apache
RewriteRule ^([0-9]+(\.[0-9]+)*)$ https://example.org/$1/vocab.ttl [R=303,L]
```

Dots in the **substitution** are literal and need no escaping — only the pattern
is a regular expression.

## How to fix

Escape every `.` in the pattern that is meant to be a literal dot.

A dot that is a deliberate wildcard is not reported: `(.*)`, `(.+)` and `.?`
are all doing their job, and so is a dot inside a character class. The check
looks only for a dot followed by something shaped like a file extension, which
is where the mistake actually lives.

## How to check

Run `w3id-check` with `--rule htaccess/escape-literal-dots` to check this rule
on its own; without it the tool runs every rule, as the pull request checks do.

```sh
node tools/check/bin/w3id-check.js --rule htaccess/escape-literal-dots ids/my-project
```

[Testing your changes](/guides/testing) covers installing the tool, and what
else is worth checking by hand.

## See also

* [`htaccess/no-greedy-capture`](./no-greedy-capture)
* [`htaccess/anchor-patterns`](./anchor-patterns)
* [Report a false positive or a missing check](https://github.com/perma-id/w3id.org/issues)
