Ever dreamt of customising your nginx error pages?
A file dump.
nginx_errors.sed
# patch for src/http/ngx_http_special_response.c error HTML strings
# (run with `sed -n`)
# Original source¹ goes like:
# L1 static char ngx_http_error_404_page[] =
# L2 "<html>" CRLF
# L3 "<head><title>404 Not Found</title></head>" CRLF
# L4 "<body>" CRLF
# L5 "<center><h1>404 Not Found</h1></center>" CRLF
# ;
#
# (lines 60-337)
40,360 {
N; # Append the next line to the pattern space for look-ahead
# Little whitespace at the top [L4]
s|<body>"|<body style=\\"padding-top: 8rem\\">"|;
# Change error message font to mono-space [L5]
s|<h1>([^<]+)</h1>|<h1><tt>\1</tt></h1>|;
# Redish background for HTTP status >= 400
# Change current line [L4] if next line matches [L5] - both after previous patching
/<h1><tt>[^123]/ {
# Tailwind red-300
s|rem\\"|rem; background: oklch(80.8% 0.114 19.571)\\"|;
}
# British l10n [L3] [L5]
s|Authorization|Authorisation|;
P; # Print the first line of the two-line pattern space
D; # Delete the first line and restart cycle with the second line
}
# static u_char ngx_http_error_tail[] =
# "<hr><center>nginx</center>" CRLF
# "</body>" CRLF
# "</html>" CRLF
# ;
#
# (lines 21-39)
1,60 {
# Remove "nginx" branding from error pages
/<hr><center>/d;
}
p;
# ¹ https://github.com/nginx/nginx/blob/master/src/http/ngx_http_special_response.c
Nix makes it easy to apply it to the upstream package automatically on every update:
nixpkgs.overlays = [
(_final: prev: {
nginxStable = prev.nginxStable.overrideAttrs (oldAttrs: {
postPatch =
(oldAttrs.postPatch or "")
+ ''
sed -i -nEf ${./www/nginx_errors.sed} src/http/ngx_http_special_response.c
'';
});
})
];
Thinking about it, let’s rather say simple instead of easy in the context of Nix.
FWIW, the world needed a (certainly bug-laden) TextMate grammar for sed
(using it for Shiki here):
sed.json
{
"displayName": "sed",
"name": "sed",
"scopeName": "source.sed",
"patterns": [
{ "include": "#comment" },
{ "include": "#instruction" }
],
"repository": {
"comment": {
"match": "#.*$",
"name": "comment.line.number-sign.sed"
},
"instruction": {
"begin": "(^\\s*)((?=[0-9$/]))?",
"end": "$",
"beginCaptures": {
"1": { "name": "leading-whitespace" }
},
"patterns": [
{ "include": "#comment" },
{
"begin": "(?=[0-9$/])",
"end": "(?=\\s*!?\\s*([a-zA-Z{]|$))",
"name": "meta.address-range.sed",
"patterns": [
{ "include": "#address_single" },
{ "match": ",", "name": "punctuation.separator.sed" }
]
},
{ "include": "#command" }
]
},
"address_single": {
"patterns": [
{
"match": "\\d+",
"name": "constant.numeric.address.sed"
},
{
"match": "\\$",
"name": "variable.language.sed"
},
{
"match": "(/)((?:\\\\/|[^/])*)(/)",
"captures": {
"1": { "name": "punctuation.definition.separator.sed" },
"2": {
"name": "string.regexp.sed",
"patterns": [
{ "include": "#escaped_char" },
{ "include": "#char_class" }
]
},
"3": { "name": "punctuation.definition.separator.sed" }
}
}
]
},
"command": {
"patterns": [
{ "include": "#substitution" },
{ "include": "#command_block" },
{
"match": "(a|b|c|d|D|g|G|h|H|i|l|n|N|p|P|q|Q|r|t|T|w|W|x|y|z|=|!|:)",
"name": "support.function.sed"
}
]
},
"command_block": {
"begin": "\\s*({)",
"end": "}",
"beginCaptures": {
"1": { "name": "punctuation.section.block.begin.sed" }
},
"endCaptures": {
"0": { "name": "punctuation.section.block.end.sed" }
},
"name": "meta.block.sed",
"patterns": [
{ "include": "#comment" },
{ "include": "#instruction" },
{ "match": ";", "name": "punctuation.separator.sed" }
]
},
"substitution": {
"name": "meta.substitution.sed",
"match": "(s)([^\\w\\s])((?:\\\\.|[^\\\\])*?)\\2((?:\\\\.|[^\\\\])*?)\\2([giIpPw]*)",
"captures": {
"1": { "name": "support.function.sed" },
"2": { "name": "punctuation.definition.separator.sed" },
"3": {
"name": "string.regexp.sed",
"patterns": [
{ "include": "#escaped_char" },
{ "include": "#char_class" }
]
},
"4": {
"name": "string.replacement.sed",
"patterns": [
{ "include": "#escaped_char" }
]
},
"5": { "name": "keyword.other.option.sed" }
}
},
"escaped_char": {
"match": "\\\\.",
"name": "constant.character.escape.sed"
},
"char_class": {
"begin": "\\[",
"beginCaptures": { "0": { "name": "punctuation.definition.character-class.begin.sed" } },
"end": "(?<!:)\\]",
"endCaptures": { "0": { "name": "punctuation.definition.character-class.end.sed" } },
"name": "constant.other.character-class.set.sed",
"patterns": [
{
"match": "\\^",
"name": "keyword.operator.negation.sed"
},
{
"match": "\\[:[a-zA-Z]+:\\]",
"name": "constant.other.posix-class.sed"
},
{ "include": "#escaped_char" }
]
}
}
}