diff --git a/src/Highlighter.php b/src/Highlighter.php
index 7f97a2c..461e85a 100644
--- a/src/Highlighter.php
+++ b/src/Highlighter.php
@@ -21,6 +21,7 @@
use Tempest\Highlight\Languages\JavaScript\JavaScriptLanguage;
use Tempest\Highlight\Languages\Json\JsonLanguage;
use Tempest\Highlight\Languages\Markdown\MarkdownLanguage;
+use Tempest\Highlight\Languages\Nginx\NginxLanguage;
use Tempest\Highlight\Languages\Php\PhpLanguage;
use Tempest\Highlight\Languages\Python\PythonLanguage;
use Tempest\Highlight\Languages\Scss\ScssLanguage;
@@ -69,6 +70,7 @@ public function __construct(private readonly Theme $theme = new CssTheme())
->addLanguage(new JavaScriptLanguage())
->addLanguage(new JsonLanguage())
->addLanguage(new MarkdownLanguage())
+ ->addLanguage(new NginxLanguage())
->addLanguage(new PhpLanguage())
->addLanguage(new PythonLanguage())
->addLanguage(new ScssLanguage())
diff --git a/src/Languages/Nginx/NginxLanguage.php b/src/Languages/Nginx/NginxLanguage.php
new file mode 100644
index 0000000..cda7ade
--- /dev/null
+++ b/src/Languages/Nginx/NginxLanguage.php
@@ -0,0 +1,68 @@
+on|off)\b';
+ }
+
+ public function getTokenType(): TokenTypeEnum
+ {
+ return TokenTypeEnum::VALUE;
+ }
+}
diff --git a/src/Languages/Nginx/Patterns/NginxDirectivePattern.php b/src/Languages/Nginx/Patterns/NginxDirectivePattern.php
new file mode 100644
index 0000000..1e4a5f0
--- /dev/null
+++ b/src/Languages/Nginx/Patterns/NginxDirectivePattern.php
@@ -0,0 +1,45 @@
+(?:{$directives}))\b";
+ }
+
+ public function getTokenType(): TokenTypeEnum
+ {
+ return TokenTypeEnum::KEYWORD;
+ }
+}
diff --git a/src/Languages/Nginx/Patterns/NginxOperatorPattern.php b/src/Languages/Nginx/Patterns/NginxOperatorPattern.php
new file mode 100644
index 0000000..b1a05a5
--- /dev/null
+++ b/src/Languages/Nginx/Patterns/NginxOperatorPattern.php
@@ -0,0 +1,27 @@
+~\*|[;{}~=^])';
+ }
+
+ public function getTokenType(): TokenTypeEnum
+ {
+ return TokenTypeEnum::OPERATOR;
+ }
+}
diff --git a/src/Languages/Nginx/Patterns/NginxVariablePattern.php b/src/Languages/Nginx/Patterns/NginxVariablePattern.php
new file mode 100644
index 0000000..ed69d68
--- /dev/null
+++ b/src/Languages/Nginx/Patterns/NginxVariablePattern.php
@@ -0,0 +1,27 @@
+\$[a-zA-Z_]\w*)';
+ }
+
+ public function getTokenType(): TokenTypeEnum
+ {
+ return TokenTypeEnum::VARIABLE;
+ }
+}
diff --git a/tests/Bench/Fixtures/nginx.txt b/tests/Bench/Fixtures/nginx.txt
new file mode 100644
index 0000000..f49cdac
--- /dev/null
+++ b/tests/Bench/Fixtures/nginx.txt
@@ -0,0 +1,74 @@
+# Main Nginx configuration
+worker_processes auto;
+error_log /var/log/nginx/error.log;
+
+events {
+ worker_connections 1024;
+}
+
+http {
+ include /etc/nginx/mime.types;
+ default_type application/octet-stream;
+
+ sendfile on;
+ tcp_nopush on;
+ tcp_nodelay on;
+ keepalive_timeout 65;
+ gzip on;
+ gzip_types text/plain application/json;
+
+ log_format main '$remote_addr - $remote_user [$time_local] '
+ '"$request" $status $body_bytes_sent';
+
+ upstream backend {
+ server 127.0.0.1:8080;
+ server 127.0.0.1:8081;
+ }
+
+ server {
+ listen 80;
+ server_name example.com www.example.com;
+ return 301 https://$host$request_uri;
+ }
+
+ server {
+ listen 443 ssl;
+ server_name example.com;
+
+ ssl_certificate /etc/ssl/certs/example.pem;
+ ssl_certificate_key /etc/ssl/private/example.key;
+ ssl_protocols TLSv1.2 TLSv1.3;
+
+ root /var/www/html;
+ index index.html index.htm;
+
+ add_header X-Frame-Options "SAMEORIGIN";
+ add_header X-Content-Type-Options "nosniff";
+
+ location / {
+ try_files $uri $uri/ =404;
+ }
+
+ location /api {
+ proxy_pass http://backend;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_http_version 1.1;
+ }
+
+ location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
+ expires 30d;
+ access_log off;
+ }
+
+ location = /health {
+ stub_status;
+ allow 127.0.0.1;
+ deny all;
+ }
+
+ if ($request_uri ~* "^/old-path") {
+ rewrite ^/old-path(.*)$ /new-path$1 permanent;
+ }
+ }
+}
diff --git a/tests/Bench/HighlighterBench.php b/tests/Bench/HighlighterBench.php
index 23685c5..ef4f42d 100644
--- a/tests/Bench/HighlighterBench.php
+++ b/tests/Bench/HighlighterBench.php
@@ -31,6 +31,7 @@ final class HighlighterBench
'javascript' => 'javascript.txt',
'json' => 'json.txt',
'markdown' => 'markdown.txt',
+ 'nginx' => 'nginx.txt',
'php' => 'php.txt',
'python' => 'python.txt',
'scss' => 'scss.txt',
diff --git a/tests/Languages/Nginx/NginxLanguageTest.php b/tests/Languages/Nginx/NginxLanguageTest.php
new file mode 100644
index 0000000..a970b90
--- /dev/null
+++ b/tests/Languages/Nginx/NginxLanguageTest.php
@@ -0,0 +1,108 @@
+assertSame(
+ $expected,
+ $highlighter->parse($content, 'nginx'),
+ );
+
+ $this->assertSame(
+ $expected,
+ $highlighter->parse($content, 'nginxconf'),
+ );
+ }
+
+ public static function provide_highlight_cases(): iterable
+ {
+ return [
+ [<<<'TXT'
+# Reverse proxy configuration
+server {
+ listen 80;
+ server_name example.com;
+
+ location / {
+ proxy_pass http://backend;
+ proxy_set_header Host $host;
+ }
+
+ location ~* \.(jpg|css|js)$ {
+ expires 30d;
+ gzip on;
+ }
+}
+TXT,
+ <<<'TXT'
+
+server {
+ listen 80;
+ server_name example.com;
+
+ location / {
+ proxy_pass http://backend;
+ proxy_set_header Host $host;
+ }
+
+ location ~* \.(jpg|css|js)$ {
+ expires 30d;
+ gzip on;
+ }
+}
+TXT],
+ [<<<'TXT'
+upstream backend {
+ server 127.0.0.1:8080;
+ server 127.0.0.1:8081;
+ keepalive_timeout 65;
+}
+TXT,
+ <<<'TXT'
+upstream backend {
+ server 127.0.0.1:8080;
+ server 127.0.0.1:8081;
+ keepalive_timeout 65;
+}
+TXT],
+ [<<<'TXT'
+server {
+ listen 443 ssl;
+ ssl_certificate /etc/ssl/cert.pem;
+ ssl_certificate_key /etc/ssl/key.pem;
+
+ add_header X-Frame-Options "SAMEORIGIN";
+
+ if ($request_uri ~* "^/old") {
+ return 301 $scheme://example.com/new;
+ }
+}
+TXT,
+ <<<'TXT'
+server {
+ listen 443 ssl;
+ ssl_certificate /etc/ssl/cert.pem;
+ ssl_certificate_key /etc/ssl/key.pem;
+
+ add_header X-Frame-Options "SAMEORIGIN";
+
+ if ($request_uri ~* "^/old") {
+ return 301 $scheme://example.com/new;
+ }
+}
+TXT],
+ ];
+ }
+}