Skip to content

Rule catalogue ​

Each rule on this page describes one specific, checkable thing that contributions to this repository get wrong. They exist because these mistakes recur — most of them have needed repeated corrective commits over the years.

Every rule has a stable URL of the form https://w3id.org/docs/rules/<id>, so a reviewer or an automated check can link to the exact explanation rather than restating it.

If you are looking for a walkthrough rather than a reference, start with the guides.

How to read a rule ​

FieldMeaning
idStable identifier. Also the page's URL and how checks refer to it.
severityerror — the identifier is broken or the change is not acceptable. warning — it works, but it will cause problems. notice — advisory.
statusenforced — an automated check emits this id. proposed — documented, not yet checked.
applies-toWhich files the rule concerns, as a reader-facing summary. The checker's own patterns are the source of truth.

The three severities are the three annotation levels GitHub Actions understands, so a finding keeps the same word from the checker's output through to the pull request.

Running the checks ​

The checker lives in tools/check/ and needs no arguments:

sh
(cd tools/check && npm ci)   # once
node tools/check/bin/w3id-check.js

It compares your branch against upstream/master if you have an upstream remote, and origin/master otherwise. It counts work you have not committed yet, and reports only what your own change is answerable for — the repository's existing backlog stays out of your way. To look at one directory:

sh
node tools/check/bin/w3id-check.js ids/my-project

Only error findings block a pull request. A warning is something a maintainer will probably ask about; a notice is there so you have seen it.

The existing repository is not a style guide

There are thousands of .htaccess files here, written by more than a thousand people over more than a decade, and many are copies of copies. That a pattern appears in hundreds of files is evidence it was copied, not that it is correct — htaccess/valid-cors-header documents a broken line that spread to hundreds of files exactly that way.

These rules are maintainer guidance. Existing files are not.

The rules are not infallible either. If one is wrong about your files, or if something that should have been caught was not, please open an issue. A false positive is a bug in the rule rather than something to work around, and a check that should exist is hardest to notice from the inside — the person best placed to report it is whoever has just watched their own mistake pass.

Files and directories ​

RuleSeverityWhat
files/only-allowed-nameserrorOnly .htaccess and a README — no content files, and the rules file must be named exactly
files/no-empty-htaccesserrorAn .htaccess with no directives resolves to 404
files/no-executable-biterrorA file that is not a program must not be executable
files/htaccess-requiredwarningSomething in the directory must answer requests
files/prefer-readme-mdwarningWrite it as Markdown, called README.md

Repository shape ​

RuleSeverityWhat
tree/identifier-under-idserrorIdentifier directories must be under ids/
tree/no-case-collisionerrorTwo names differing only by case cannot both be checked out
tree/only-own-identifierwarningKeep a change to one identifier

.htaccess syntax ​

RuleSeverityWhat
htaccess/no-flag-whitespaceerrorNo spaces inside the [...] flag list
htaccess/space-before-flagserrorA space before the [...], or it is part of the target
htaccess/valid-rewrite-flagserrorFlag names must be ones mod_rewrite knows
htaccess/no-inline-commenterror# only starts a comment at the start of a line
htaccess/uppercase-rewrite-flagswarningWrite flags in upper case

Rules that never match ​

RuleSeverityWhat
htaccess/rewrite-engine-requirederrorRewriteRule does nothing without RewriteEngine on
htaccess/pattern-relative-to-direrrorNo leading slash, no repeated directory name
htaccess/anchor-patternswarningMake an optional trailing group optional
htaccess/no-greedy-capturewarning^(.+)/?$ swallows the slash
htaccess/escape-literal-dotswarning. is a wildcard; escape a literal dot

Redirect targets ​

RuleSeverityWhat
htaccess/no-open-redirecterrorNever build the target hostname from the request
htaccess/https-targetwarningRedirect to https://
htaccess/github-raw-targetwarningRaw file URLs, not /blob/ pages
htaccess/no-rawgiterrorRawGit has shut down; use jsDelivr
htaccess/no-double-slashwarningNo // in the produced URL
htaccess/no-self-redirectwarningPoint at the destination, not back at w3id.org
htaccess/avoid-permanent-redirectwarningA 301 cannot be taken back

Directives and negotiation ​

RuleSeverityWhat
htaccess/allowed-directiveserrorNo code execution, proxying, or access control
htaccess/valid-cors-headerwarningDo not copy the truncated CORS line
htaccess/no-406-fallbackwarningEnding negotiation in 406 answers nobody
htaccess/no-options-directiveproposedOptions directives — open questions

File format ​

RuleSeverityWhat
format/no-bomerrorNo byte order mark — it makes Apache return 500
format/no-crlfwarningUnix line endings, not CRLF
format/final-newlinewarningEnd the file with a newline
format/no-trailing-whitespacewarningNo trailing whitespace that does nothing
format/no-excessive-blank-lineswarningNo long runs of blank lines, and none at the start or end

Markdown ​

RuleSeverityWhat
markdown/prefer-list-over-line-breaksnoticeA stack of manual line breaks reads better as a list

Pull request shape ​

These look at the commits rather than the files, so they are reported on a pull request and not by a run scoped to a directory.

RuleSeverityWhat
git/minimal-commitswarningOne identifier change should be one commit
git/no-merge-commitswarningRebase; do not merge master into your branch
git/descriptive-commit-messagewarningName the identifier, not the filename
git/no-session-linkwarningNo links to a private agent session
git/branch-not-stalenoticeStart from a recent master

Metadata ​

RuleSeverityWhat
meta/document-identifier-rootwarningClaim the root of an identifier with a maintainer record
meta/maintainer-github-usernamewarningRecord who maintains the identifier, with a GitHub username
meta/rule-docs-existwarningEvery rule has a page and every page has a rule

For tooling ​

Every rule page carries YAML frontmatter with id, title, severity, status, and applies-to. The id is always the page's path relative to docs/rules/ with the .md removed — so docs/rules/htaccess/https-target.md has the id htaccess/https-target — and a checker can enumerate the rule set from docs/rules/**/*.md and emit https://w3id.org/docs/rules/<id> in its output.

Pages named index.md are catalogue pages, not rules, and carry no id.

That contract is itself checked, by meta/rule-docs-exist: every rule the checker implements must have a page, every page claiming status: enforced must have a rule behind it, and a page's id must match its path.

applies-to is outside that contract. It is a summary written for readers, and nothing verifies it against the patterns the rule actually selects on, so do not use it to decide which files to check — read the rule. The frontmatter fields safe to build on are the ones above: id, severity and status.

Raw Markdown for any page is available by appending .md to its URL, and the whole site is published as llms.txt and llms-full.txt.