htaccess/no-greedy-capture
Severity: warning · Status: enforced · Applies to: ids/**/.htaccess
What
^(.+)/?$ does not do what it appears to. Where you mean "one path segment", write ([^/]+).
Why
In ^(.+)/?$, the .+ is greedy, so it consumes as much as it can — including the trailing slash — before the optional /? gets a chance to match anything. For a request to vocab/, the capture $1 ends up as vocab/ rather than vocab.
Substitute that into a URL with its own slash and you get a doubled separator and a broken path:
RewriteRule ^(.+)/?$ https://example.org/data/$1/thing.ttl
vocab/ → https://example.org/data/vocab//thing.ttlThis exact pattern produced malformed ontology redirects in this repository and had to be corrected.
Wrong
RewriteRule ^(.+)/?$ https://example.org/data/$1/vocab.ttl [R=303,L]Right
# one segment, trailing slash optional and not captured
RewriteRule ^([^/]+)/?$ https://example.org/data/$1/vocab.ttl [R=303,L]When you genuinely want to pass an arbitrary path straight through, (.*) is correct — just do not add slashes around it. That form is not reported:
RewriteRule ^(.*)$ https://example.org/base/$1 [R=302,L]How to fix
Ask what $1 should contain, then pick the narrowest expression that produces it:
| You mean | Use |
|---|---|
| one path segment | ([^/]+) |
| one optional path segment | ([^/]*) |
| a version number | ([0-9]+(\.[0-9]+)*) |
| a known file extension | (ttl|jsonld|rdf|nt) |
| the whole remaining path, unchanged | (.*) |
Then check the result for doubled slashes — see htaccess/no-double-slash.
How to check
Run w3id-check with --rule htaccess/no-greedy-capture to check this rule on its own; without it the tool runs every rule, as the pull request checks do.
node tools/check/bin/w3id-check.js --rule htaccess/no-greedy-capture ids/my-projectOnly the ^(.+)/?$ shape is reported. Deciding that some other (.+) "should have been" ([^/]+) needs to know what the author meant.
Testing your changes covers installing the tool, and what else is worth checking by hand.