Біль: ваш сервер «працював учора», ви встановили «лише одну річ», і тепер порт 80 «вже використовується», TLS-редиректи крутяться по колу або Nginx проксірує… сам себе. Нічого не палає, але графік uptime пітніє.
Ubuntu 24.04 робить простим встановлення обох серверів — Apache і Nginx — іноді без вашої явної згоди. Трюк не в «перезапускайте все, поки не запрацює». Трюк у тому, щоб вирішити, хто володіє яким портом, а потім зробити так, щоб проксі та заголовки говорили правду.
Швидкий план діагностики
Коли на сервері присутні Apache і Nginx, найшвидший шлях — відповісти на три запитання по порядку. Не чіпайте конфіги, доки не зможете відповісти з доказами.
1) Хто насправді слухає на 80 і 443?
Якщо ви не знаєте, який процес має сокети, ви гадатимете. Гадання — це шлях створити «тимчасові фікси», що виживуть роками.
- Перевірте слухачів за допомогою
ssі зіставлення PID із сервісами. - Якщо бачите
apache2іnginx, які обидва намагаються володіти:80, ви вже знайшли перший розлом.
2) Куди йде трафік після першого accept()?
Підтвердіть, чи Nginx робить reverse proxy до Apache (поширено), Apache проксірує кудись ще, або обидва знаходяться в петлі проксі.
- Використайте
curl -v, щоб побачити заголовкиLocation:, коди стану й чи неправильно виявляється HTTPS. - Перегляньте активні віртуальні хости на обох серверах (
apachectl -S,nginx -T).
3) Чи базуються редиректи й рішення про «схему» на реальності?
Більшість проксі-петель — це не «мережеві проблеми». Це питання правди: бекенд думає, що запит прийшов по HTTP, коли клієнт використав HTTPS, тому він постійно редиректить «на HTTPS».
- Виправте заголовки:
X-Forwarded-Proto,X-Forwarded-Hostі (якщо треба) обробкуRemoteIPв Apache. - Відкоригуйте очікування бекенду щодо портів і схеми (виртуальний хост Apache на 127.0.0.1:8080, а не на 80).
Контроль прийняття рішення: Якщо ви не можете описати шлях запиту в одному реченні («Клієнт потрапляє на Nginx :443, проксі до Apache :8080 через loopback, Apache обслуговує додаток з правильними заголовками схеми»), зупиніться і змалюйте це. Жодних змін, поки ви не можете це переказати.
Цікаві факти та контекст (чому це повторюється)
- Nginx створювали для проблеми C10k. Подієво-орієнтований дизайн Ігоря Сисоєва (початок 2000-х) став стандартним вибором для фронтендів з великою кількістю одночасних з’єднань.
- Моделі процесів/потоків Apache еволюціонували під різні епохи. Prefork, worker і event MPM існують, бо «один розмір підходить всім» ніколи не працював у продакшені.
- Ubuntu історично робила Apache сервером за замовчуванням. Ця пам’ять мʼязів зберігається; багато пакетів припускають наявність Apache або можливість його ввімкнення.
- systemd ввів socket activation як first-class механізм. Він може тримати порт відкритим і запускати сервіс за потребою, що ускладнює діагностику «хто слухає?», якщо не перевіряти типи юнітів.
- Заголовки зворотного проксі — де-факто стандарт, а не єдина специфікація.
X-Forwarded-*широко використовується, але додатки та фреймворки інтерпретують його по-різному. - «Занадто багато редиректів» зазвичай детерміноване явище. Воно здається випадковим через кеші та HSTS, але петлю можна відтворити з
curl -v. - Порт 80 особливий, бо всі його хочуть. Каптив-портали, ACME HTTP-01 challenges, сайти за замовчуванням і «тимчасові сервери для налагодження» змагаються за нього.
- Культура Apache з
a2enmod/a2ensiteвідрізняється від Nginx зі symlink’ами вsites-enabled. Змішання ментальних моделей породжує «я змінив файл, чому це не вплинуло?» ситуації.
Ментальна модель: bind, accept, proxy, redirect
Плутанина в веб-стеку рідко про «який сервер кращий». Вона про володіння сокетами і про ясність намірів.
Bind: лише один процес володіє TCP-портом
На звичайному Linux-хості 0.0.0.0:80 може бути прив’язаний рівно одним процесом одночасно. Якщо ви запускаєте Apache після того, як Nginx вже прив’язав :80, Apache зазнає невдачі (або навпаки). Якщо systemd тримає порт для socket activation, обидва можуть «виглядати як запущені», але один не зможе реально приймати трафік.
Accept: перший сервер задає тон
Сервер, що прийняв зʼєднання, контролює TLS-термінацію, HTTP/2 vs HTTP/1.1, логування доступу на краю і рішення про «реальний IP клієнта». Все за ним — це бекенд. Ставтеся до нього як до edge-рівня, навіть якщо він на тій же VM.
Proxy: в одному напрямку, без бумерангів
Reverse proxy має відправляти трафік на інший порт або інший хост. Найтривіальніша шлях до випадкової петлі — проксі на хостнейм, який знову резолвиться у той же слухач (або на той самий порт через NAT). «Працює на моєму ноуті» перетворюється на «плавиться в продакшені», бо DNS і /etc/hosts відрізняються.
Redirect: додаток має знати, що бачив клієнт
Бекенди часто генерують абсолютні редиректи на основі сприйнятої схеми і хоста. Якщо TLS завершується на Nginx, а Apache бачить plain HTTP на 127.0.0.1:8080, Apache (або додаток) може наполягати «вам потрібно HTTPS» і редиректити на HTTPS. Браузер повертається до Nginx HTTPS, який знову форвардить HTTP, і бекенд знову редиректить. Ось ваша петля.
Є дві чесні істини, які ви можете навчити свій бекенд:
- Повідомте його про оригінальну схему через
X-Forwarded-Proto: https. - Або змусьте бекенд говорити HTTPS теж (рідше локально; зазвичай непотрібно).
Одне зауваження, одне життєве правило: Парафраз Андрю С. Грова «Тільки параноїки виживають» корисний для опсів: припускайте, що ваші дефолти зрадять вас, якщо їх не перевірити.
Практичні завдання (команди, очікуваний вивід, рішення)
Це реальні завдання, які можна виконати на Ubuntu 24.04 для діагностики й виправлення привʼязки портів та проксі-петель. Кожне завдання включає: команду, значення виводу та рішення, яке варто прийняти.
Завдання 1: Визначте, хто слухає на 80/443 (єдина відправна точка)
cr0x@server:~$ sudo ss -ltnp '( sport = :80 or sport = :443 )'
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=1274,fd=6))
LISTEN 0 511 0.0.0.0:443 0.0.0.0:* users:(("nginx",pid=1274,fd=7))
Що це означає: Nginx володіє обома сокетами. Apache не є edge-рівнем, навіть якщо встановлений.
Рішення: Якщо ви хочете, щоб Apache був за Nginx, відсуньте Apache з 80/443 (наприклад, на 8080). Якщо хочете лише Apache, зупиніть/відключіть Nginx.
Завдання 2: Перевірте, чи Apache падає через помилку привʼязки
cr0x@server:~$ sudo journalctl -u apache2 -n 50 --no-pager
Aug 14 09:12:10 server apachectl[2219]: (98)Address already in use: AH00072: make_sock: could not bind to address 0.0.0.0:80
Aug 14 09:12:10 server apachectl[2219]: AH00451: no listening sockets available, shutting down
Aug 14 09:12:10 server apachectl[2219]: AH00015: Unable to open logs
Що це означає: Apache налаштований слухати на порті 80 і не може цього зробити.
Рішення: Або зупиніть інший слухач (часто Nginx), або змініть директиву Listen в Apache на бекенд-порт.
Завдання 3: Підтвердіть стани systemd-юнітів (enabled vs running vs socket-activated)
cr0x@server:~$ systemctl status nginx --no-pager
● nginx.service - A high performance web server and a reverse proxy server
Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; preset: enabled)
Active: active (running) since Wed 2025-08-14 09:10:22 UTC; 4min 18s
Docs: man:nginx(8)
Main PID: 1274 (nginx)
Tasks: 3 (limit: 18931)
Memory: 6.8M
CPU: 78ms
cr0x@server:~$ systemctl status apache2 --no-pager
● apache2.service - The Apache HTTP Server
Loaded: loaded (/usr/lib/systemd/system/apache2.service; enabled; preset: enabled)
Active: failed (Result: exit-code) since Wed 2025-08-14 09:12:10 UTC; 2min 30s ago
Main PID: 2219 (code=exited, status=1/FAILURE)
Що це означає: Nginx здоровий; Apache падає. Це не ситуація «перезапустіть обидва»; це архітектурне рішення.
Рішення: Оберіть топологію: (A) тільки Nginx, (B) тільки Apache, (C) Nginx як edge + Apache як бекенд. Потім налаштуйте відповідно.
Завдання 4: Перелік директив Listen і ввімкнених сайтів Apache
cr0x@server:~$ grep -R --line-number -E '^\s*Listen\s+' /etc/apache2
/etc/apache2/ports.conf:5:Listen 80
/etc/apache2/ports.conf:9:Listen 443
cr0x@server:~$ sudo apachectl -S
VirtualHost configuration:
*:80 is a NameVirtualHost
default server 000-default (/etc/apache2/sites-enabled/000-default.conf:1)
port 80 namevhost example.internal (/etc/apache2/sites-enabled/example.conf:1)
*:443 example.internal (/etc/apache2/sites-enabled/example-le-ssl.conf:2)
ServerRoot: "/etc/apache2"
Main DocumentRoot: "/var/www/html"
Main ErrorLog: "/var/log/apache2/error.log"
Що це означає: Apache налаштований привʼязуватися до 80 і 443. Якщо Nginx — edge, це потрібно змінити.
Рішення: Відредагуйте /etc/apache2/ports.conf і відповідні vhost’и, щоб привʼязуватися до 127.0.0.1:8080 (та, можливо, 8443). Або вимкніть vhost’и Apache, якщо він не використовується.
Завдання 5: Вивантажте ефективну конфігурацію Nginx, щоб побачити справжні include і upstream
cr0x@server:~$ sudo nginx -T
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
# configuration file /etc/nginx/nginx.conf:
http {
include /etc/nginx/mime.types;
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
Що це означає: Не можна довіряти «я змінив один файл», якщо ви не знаєте, що підключається. nginx -T скаже правду.
Рішення: Знайдіть, де визначено proxy_pass, і перевірте, що воно вказує на бекенд-порт, який відрізняється від слухача.
Завдання 6: Перевірте класичну помилку самопроксі (проксі на той самий хост/порт)
cr0x@server:~$ sudo grep -R --line-number -E 'proxy_pass\s+http' /etc/nginx/sites-enabled /etc/nginx/conf.d
/etc/nginx/sites-enabled/example.conf:18: proxy_pass http://127.0.0.1:80;
Що це означає: Якщо Nginx слухає на 80 і проксірує на 127.0.0.1:80, він проксірує сам себе. Це петля, яка виглядає як зависання, 502 або підвищене навантаження CPU.
Рішення: Змініть бекенд на інший порт (наприклад, Apache на 127.0.0.1:8080) або на іншу ціль взагалі.
Завдання 7: Перевірте досяжність бекенду (відокремте конектність від HTTP-поведінки)
cr0x@server:~$ curl -sS -D- http://127.0.0.1:8080/ -o /dev/null
HTTP/1.1 200 OK
Date: Wed, 14 Aug 2025 09:17:02 GMT
Server: Apache/2.4.58 (Ubuntu)
Content-Type: text/html; charset=UTF-8
Що це означає: Apache доступний на 8080 і повертає контент. Добре. Тепер ваше проксі може вказувати туди.
Рішення: Якщо це падає, виправляйте Apache спочатку (порти, vhost, стан сервісу, брандмауер). Не «налаштовуйте Nginx», коли бекенд мертвий.
Завдання 8: Зловіть петлю редиректів через заголовки (ви не виправите те, що не бачите)
cr0x@server:~$ curl -k -I https://example.internal/
HTTP/2 301
date: Wed, 14 Aug 2025 09:18:41 GMT
location: http://example.internal/
server: nginx
cr0x@server:~$ curl -I http://example.internal/
HTTP/1.1 301 Moved Permanently
Date: Wed, 14 Aug 2025 09:18:45 GMT
Location: https://example.internal/
Server: nginx
Що це означає: HTTPS редиректить на HTTP, а HTTP — на HTTPS. Це двовузлова петля. Зазвичай це викривлені правила редиректу або конфліктні конфіги в різних vhost’ах.
Рішення: Оберіть канонічну схему (зазвичай HTTPS) і застосуйте її в одному місці (зазвичай на edge). Приберіть суперечливі редиректи в бекенді.
Завдання 9: Перегляньте заголовки, які Nginx пересилає (схема й хост — часті винуватці)
cr0x@server:~$ sudo grep -n 'proxy_set_header' -n /etc/nginx/sites-enabled/example.conf
22: proxy_set_header Host $host;
23: proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
24: proxy_set_header X-Forwarded-Proto $scheme;
Що це означає: Якщо X-Forwarded-Proto не встановлено, багато додатків неправильно визначать HTTPS і згенерують хибні редиректи або змішаний контент.
Рішення: Переконайтесь, що встановлені Host і X-Forwarded-Proto. Якщо TLS завершується на Nginx, $scheme має бути https для клієнтських HTTPS-запитів.
Завдання 10: Перевірте, чи Apache бачить переслану схему і не «підвищує» знову
cr0x@server:~$ sudo grep -R --line-number -E 'RewriteRule|RewriteCond|HTTPS|X-Forwarded-Proto' /etc/apache2/sites-enabled
/etc/apache2/sites-enabled/example.conf:12:RewriteCond %{HTTPS} !=on
/etc/apache2/sites-enabled/example.conf:13:RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
Що це означає: Apache виконує HTTPS-редирект на основі свого власного змінного %{HTTPS}. За SSL-термінуючим проксі Apache вважатиме HTTPS вимкненим.
Рішення: Приберіть цей редирект з Apache, коли Nginx — edge, або перепишіть умову так, щоб вона враховувала X-Forwarded-Proto, щоб бекенд не боровся з фронтом.
Завдання 11: Перевірте, чи дубльовані/дефолтні сайти не перекривають ваш хост
cr0x@server:~$ ls -l /etc/nginx/sites-enabled
total 0
lrwxrwxrwx 1 root root 34 Aug 14 09:05 default -> /etc/nginx/sites-available/default
lrwxrwxrwx 1 root root 34 Aug 14 09:06 example.conf -> /etc/nginx/sites-available/example.conf
Що це означає: Дефолтний Nginx-сайт може захоплювати трафік, який ви очікували віддати кудись інше, залежно від збігу server_name і правил default_server.
Рішення: Вимкніть дефолтний сайт, якщо він не потрібен, або переконайтесь, що ваш потрібний сайт позначений default_server для відповідних listen-рядків.
Завдання 12: Підтвердіть, що «реальний» конфіг завантажено і перезавантажуйте безпечно
cr0x@server:~$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
cr0x@server:~$ sudo systemctl reload nginx
Що це означає: Синтаксис валідний, а reload застосує зміни без обриву існуючих зʼєднань (переважно це бажано).
Рішення: Вирішіть reload замість restart для Nginx/Apache при застосуванні конфігів в продакшені, якщо тільки ви не змінюєте низькорівневі модулі або сервіс не завис.
Завдання 13: Перевірте розвʼязування DNS/hosts, щоб уникнути «проксі на себе» по імені
cr0x@server:~$ getent hosts example.internal
10.10.20.15 example.internal
Що це означає: Якщо Nginx проксірує на http://example.internal і це резолвиться в публічну IP-адресу сервера, ви можете повернутися в Nginx замість потрапляння до Apache.
Рішення: Проксіруйте на 127.0.0.1 (або на виділену IP бекенду) і на бекенд-порт. Уникайте публічного імені для локальних бекендів, якщо ви явно не хочете проходити через edge-маршрутизацію.
Завдання 14: Перевірте, чи UFW не «допомагає» блокуючи ваш бекенд-порт
cr0x@server:~$ sudo ufw status verbose
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), deny (routed)
New profiles: skip
To Action From
OpenSSH ALLOW IN Anywhere
80/tcp ALLOW IN Anywhere
443/tcp ALLOW IN Anywhere
Що це означає: Порт бекенду 8080 не дозволений з інтернету, що добре. Але якщо Apache привʼязаний до 127.0.0.1:8080, UFW не має значення для loopback.
Рішення: Привʼяжіть бекенд-сервіси до loopback. Не відкривайте бекенд-порти, якщо вам справді не потрібен віддалений доступ.
Завдання 15: Підтвердіть, що Apache слухає там, де ви думаєте, після змін
cr0x@server:~$ sudo ss -ltnp | grep -E ':(80|8080|443|8443)\b'
LISTEN 0 511 127.0.0.1:8080 0.0.0.0:* users:(("apache2",pid=3012,fd=4))
LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=1274,fd=6))
LISTEN 0 511 0.0.0.0:443 0.0.0.0:* users:(("nginx",pid=1274,fd=7))
Що це означає: Це чистий розподіл, якого ви прагнете: Nginx володіє інтернет-портами, Apache залишається приватним на loopback.
Рішення: Зафіксуйте це: переконайтеся, що Apache не налаштовано привʼязуватися до публічних інтерфейсів, і що жоден інший сервіс не вхопить 8080 випадково.
Завдання 16: Шукайте причини 502/504 у логах (не дивіться лише на браузер)
cr0x@server:~$ sudo tail -n 30 /var/log/nginx/error.log
2025/08/14 09:22:33 [error] 1274#1274: *118 connect() failed (111: Connection refused) while connecting to upstream, client: 10.10.20.33, server: example.internal, request: "GET / HTTP/2.0", upstream: "http://127.0.0.1:8080/", host: "example.internal"
Що це означає: Nginx не може підключитися до upstream; Apache вимкнений, слухає в іншому місці або заблокований.
Рішення: Виправте стан бекенду і його привʼязку, а не налаштування таймаутів Nginx. Connection refused — це не проблема продуктивності.
Короткий жарт №1: Якщо ви «вирішили» конфлікт портів перезавантаженням, вітаю — ви навчили проблему ховатися краще.
Чисті шаблони виправлення (виберіть один і зафіксуйте)
Ви можете запускати Apache і Nginx разом. Ви також можете використовувати лише один. Режим відмови виникає, коли обидва працюють із перетинаючими обовʼязками і без чіткого володіння портами.
Шаблон A: Nginx як edge (80/443) + Apache бекенд (127.0.0.1:8080)
Це найпоширеніший «обидва встановлені» кінцевий стан, який залишається адекватним. Nginx обробляє TLS, HTTP/2, статичні файли за бажанням, буферизацію та просту маршрутизацію. Apache обслуговує PHP-додатки (через PHP-FPM або mod_php) або спадкові .htaccess-настроювання. Apache ніколи не торкається портів 80/443 на публічному інтерфейсі.
Apache: зсуньтеся з 80/443
Відредагуйте /etc/apache2/ports.conf приблизно так:
cr0x@server:~$ sudo sed -n '1,120p' /etc/apache2/ports.conf
# If you just change these ports or add more ports here, you will likely also
# have to change the VirtualHost statement in
# /etc/apache2/sites-enabled/000-default.conf
Listen 127.0.0.1:8080
Потім переконайтесь, що ваш vhost слухає на 8080:
cr0x@server:~$ sudo grep -n '<VirtualHost' /etc/apache2/sites-enabled/example.conf
1:<VirtualHost 127.0.0.1:8080>
Рішення: Явна привʼязка до 127.0.0.1 запобігає випадковому відкриттю і виключає питання «чомусь UFW блокує мій бекенд?».
Nginx: проксіруйте на Apache і встановіть правильні заголовки
Мінімальний, чесний server block Nginx виглядає концептуально так:
proxy_pass http://127.0.0.1:8080;- Пересилайте
Hostі схему черезX-Forwarded-Proto - За потреби встановіть
X-Forwarded-HostіX-Forwarded-Port
Рішення: Мета — щоб Apache/додаток могли точно реконструювати зовнішню URL.
Шаблон B: Тільки Apache (80/443) — просто і нудно
Якщо вам не потрібні фічі Nginx, не запускайте його «просто на всякий випадок». Простішій стек простіше налагоджувати під час нічних інцидентів.
Зробіть таке:
- Зупиніть і відключіть Nginx.
- Переконайтесь, що Apache слухає 80/443 і має правильні vhost’и.
І не робіть таке:
- Не залишайте Nginx увімкненим «на випадок, якщо знадобиться». Так ви отримуєте неприємні конфлікти портів під час оновлень пакетів.
Шаблон C: Тільки Nginx і видаліть Apache з шляху
Якщо «бекенд» — це сучасний сервер додатків (Gunicorn, uWSGI, Node тощо), Apache може бути зайвим. Nginx може проксірувати безпосередньо до додатку, і ви уникаєте зайвого переходу.
Рішення: Якщо єдиною причиною для Apache є «він був у туторіалі», забудьте той туторіал і рухайтеся далі.
Проксі-петлі та пекло редиректів: як знайти й виправити
Є дві великі класи петель:
- Мережеві/проксі-петлі: проксірування на себе (той самий хост/порт), або імʼя, що резолвиться назад на edge-слухач.
- Петлі редиректів: бекенд продовжує редиректити, бо вважає, що схема/хост/порт відрізняються від тих, що використав клієнт.
Тип петлі 1: Nginx проксірує себе
Типовий симптом: Запити зависають, CPU росте, в логах Nginx — таймаути upstream, або багато внутрішніх зʼєднань на себе.
Корінь проблеми: proxy_pass http://127.0.0.1:80, тоді як Nginx слухає на 80; або proxy_pass http://example.internal і DNS резолвить у ту саму IP-адресу, на якій слухає Nginx.
Виправлення: Переконайтесь, що upstream — інша ціль: loopback на іншому порті, Unix-socket або інший хост. Використовуйте явний IP:порт, щоб уникнути DNS-сюрпризів.
Тип петлі 2: HTTP ↔ HTTPS редирект-пінг-понг
Типовий симптом: Браузер повідомляє «занадто багато редиректів», curl -I показує чергування Location: http:// і Location: https://.
Корінь проблеми: Один шар змушує HTTPS, інший — HTTP, або бекенд змушує HTTPS, бо не розуміє, що він за TLS-термінуючим проксі.
Виправлення: Визначте одне місце для примусу. Зазвичай: Nginx виконує HTTP→HTTPS, бекенд не повинен. Повідомте бекенд про схему через X-Forwarded-Proto і налаштуйте додаток довіряти цим заголовкам.
Тип петлі 3: Канонічні редиректи хоста борються з server_name
Типовий симптом: Ви запитуєте www, відбувається редирект на apex, потім назад на www.
Корінь проблеми: Дві різні конфігурації обидві вважають, що вони керують канонізацією: Nginx rewrite + канонічна настройка на рівні додатку.
Виправлення: Вирішіть, де живе канонічний хост. Для більшості організацій: робіть це на краю (Nginx) і вимикайте канонічні редиректи в додатку, якщо не потрібні для мульти-tenant маршрутизації.
Тип петлі 4: Неправильні маршрути для ACME/Let’s Encrypt challenge
Типовий симптом: Видача сертифіката не вдається, і endpoint challenge повертає редирект або 404 від неправильного сервера.
Корінь проблеми: Обидва сервери намагаються обслужити /.well-known/acme-challenge/, або проксі надсилає цей шлях на бекенд, який робить редирект на HTTPS, а CA чекає plain HTTP.
Виправлення: Обслуговуйте ACME-challenge безпосередньо на edge HTTP vhost і обходьте бекенд для цього шляху.
Короткий жарт №2: Петля редиректів — це веб-спосіб сказати: «Я чув, вам подобається кругові подорожі».
Три корпоративні міні-історії з практики
Міні-історія 1: Інцидент через хибне припущення
Вони мігрували внутрішню панель на Ubuntu 24.04 на нову VM. Інженер, що робив cutover, інстинктивно встановив «веб-сервер». Спочатку зʼявився Apache. Пізніше того ж дня хтось додав Nginx, бо команда безпеки захотіла певний TLS-профіль, який вони використовували в інших місцях. Ніхто не видалив Apache, бо «він же нічого не ламав».
Під час зміни DNS вказали на нову VM. Трафік прийшов, і панель іноді завантажувалась. Половина разів показувався дефолтний Apache, інша половина — правильний додаток. Команда робила те, що зазвичай роблять під тиском: перезапускали сервіси, чистили кеші й звинувачували DNS propagation як погодне явище.
Корінь був нудним. Nginx був привʼязаний до 80/443, але Apache теж був увімкнений і налаштований на 80/443. Apache не зміг привʼязатися, тому падав. Проте команда додатку тестувала через локальний порт-форвард, який потрапляв на Apache з попередньої конфігурації, і вони припустили, що продакшен має такий самий шлях. Насправді — ні.
«Фікс» полягав у виборі топології й її впровадженні: Nginx як edge, Apache на 127.0.0.1:8080. Потім вони зробили health check на edge, а не на бекенді. Інцидент завершився не героїчними діями, а спільним розумінням, що припущення — не архітектура.
Міні-історія 2: Оптимізація, яка зіграла зле
Інша компанія мала спадковий PHP-додаток за Apache. Хтось захотів краще перформансу і прочитав, що Nginx «швидший». Вони поставили Nginx перед Apache як reverse proxy. Рішення розумне. Потім захотіли ще більше оптимізувати: ввімкнули агресивний кеш і додали набір rewrite-правил, щоб форсити HTTPS і канонічний хост на обох шарах «на всякий випадок».
За кілька годин користувачі почали скаржитися, що не можуть увійти. Сесії губилися, і додаток випадково редиректив між HTTP і HTTPS. Моніторинг показав стрибок 301-відповідей і підозріло малу кількість 200. Nginx-кеш гордо повертав закешовані редиректи. Оце — особливий вид самосаботажу: ви робите неправильну конфігурацію швидше.
Налагодження тривало довше, бо команда дивилася на поведінку браузера. Коли вони перейшли на curl -v і порівняли заголовки з edge і бекенду, стало очевидно. Перепис Apache наполягав на HTTPS, бо він не знав, що TLS завершується на Nginx. Nginx теж форсував HTTPS, але з трохи іншим правилом канонічного хоста. Клієнт стрибав між ними.
Виправлення — видалити половину «розумних» правил. Примус HTTPS залишили в Nginx. Apache перестав редиректити і замість цього довірився X-Forwarded-Proto для логіки схеми, де це потрібно. Правила кешування виключили редиректи й аутентифіковані шляхи. Перформанс покращився, але важливіше — поведінка стабілізувалася. Урок: оптимізація неповного розуміння лише дає неправильні відповіді швидше.
Міні-історія 3: Нудна, але правильна практика, що врятувала ситуацію
Фінтех-команда тримала Nginx як edge і Apache як бекенд для кількох старих внутрішніх інструментів. Нічого екстраординарного. Практика, що врятувала, була їхній «контракт власності портів»: невеликий внутрішній runbook, що в простих словах вказував, який сервіс привʼязується до яких портів для кожного класу хостів.
Одного дня оновлення пакетів встановило Apache на хості, де раніше був лише Nginx. Оновлення включило Apache за замовчуванням. Після ребута Apache спробував вхопити порт 80 раніше, ніж запустився Nginx. Nginx не зміг привʼязатися, і хост почав віддавати дефолтну сторінку Apache. Це могло стати неприємним інцидентом.
Але моніторинг-чеки будувалися на тому ж контракті. Вони не просто перевіряли «чи порт 80 відкритий». Вони перевіряли, що edge-відповідь містить очікуваний заголовок і що сертифікат сервера відповідає очікуваному віртуальному хосту. Алерт був специфічним: «edge identity mismatch», а не «сайт впав».
On-call дотримався runbook: перевірив слухачів, відключив Apache, перезавантажив Nginx, підтвердив через curl. Час відновлення був коротким не тому, що вони генії, а тому що вони записали нудну правду і змусили моніторинг її перевіряти.
Поширені помилки: симптом → причина → виправлення
Цей розділ навмисно конкретний. Якщо ви бачите симптом, ви маєте перейти до ймовірної причини і конкретного виправлення.
1) Apache не стартує: «Address already in use: AH00072»
- Симптоми:
systemctl status apache2показує failed; журнал показує помилку bind на 0.0.0.0:80 або :443. - Причина: Nginx (або інший сервіс) вже володіє портом; або systemd socket activation тримає його.
- Виправлення: Визначте власника 80/443. Якщо Nginx — edge, пересуньте Apache на 127.0.0.1:8080 і оновіть vhost’и. Якщо Apache — edge, зупиніть/відключіть Nginx.
2) Nginx стартує, але ви отримуєте 502 Bad Gateway
- Симптоми: Nginx повертає 502; логи показують
connect() failed (111: Connection refused)абоupstream timed out. - Причина: Upstream не слухає там, де Nginx очікує; неправильний порт; Apache вимкнений; брандмауер не має значення для loopback, якщо прив’язка невірна.
- Виправлення:
curlніть upstream напряму (127.0.0.1:8080). Виправте привʼязку і стан Apache. Потім виправтеproxy_passв Nginx.
3) Браузер: «Too many redirects»
- Симптоми: Чергування 301/302;
curl -Iпоказує стрибанняLocationзаголовків. - Причина: Примус HTTPS або канонічного хоста дублюється на шарах; бекенд не знає оригінальної схеми.
- Виправлення: Примусіть редиректи в одному місці (edge). Пересилайте
X-Forwarded-Proto. Приберіть бекенд-редиректи або зробіть їх умовними на підставі forwarded-схеми.
4) Nginx проксірує себе (тиха петля)
- Симптоми: Запити зависають; високий CPU воркерів; логи Nginx — таймаути upstream; upstream дорівнює тому самому host:port.
- Причина:
proxy_passвказує на 127.0.0.1:80, тоді як Nginx слухає на 80, або вказує на hostname, що резолвиться у той же слухач. - Виправлення: Змініть upstream на окремий бекендний порт/IP. Віддавайте перевагу loopback IP:порт або Unix-socket для локальних upstream.
5) Показується неправильний сайт (дефолтна сторінка замість вашої)
- Симптоми: Ви бачите «Apache2 Ubuntu Default Page» або привітальну сторінку Nginx несподівано.
- Причина: Увімкнений дефолтний сайт і він є дефолтним vhost; невідповідність server_name; SNI mismatch на 443.
- Виправлення: Вимкніть дефолтні сайти, які не використовуєте. Зробіть ваш хост відповідним
server_nameі позначте йогоdefault_serverсвідомо, якщо потрібно.
6) HTTP працює, HTTPS ламається (або навпаки)
- Симптоми: Одна схема працює; інша повертає 404, петлі 301 або неправильний бекенд.
- Причина: Різні vhost/серверні блоки для 80 та 443 не синхронізовані; TLS-vhost проксірує на інший upstream; невідповідність сертифікатів.
- Виправлення: Переконайтесь, що обидві схеми маршрутизуються однаково. Використовуйте HTTP тільки для редиректу на HTTPS на edge, потім уніфікуйте маршрути в HTTPS-блоці.
Чеклісти / покроковий план
Чекліст 1: Визначте топологію (перестаньте торгуватися з всесвітом)
- Чи потрібні вам фічі Nginx (налаштування HTTP/2, кешування, проста маршрутизація, edge-auth, rate limiting)? Якщо так — він має бути edge.
- Чи потрібні вам фічі Apache (спадкові .htaccess, модулі mod_* )? Якщо так — тримайте його як бекенд.
- Якщо ні, запускайте один сервер. Менше рухомих частин — легше налагоджувати.
Чекліст 2: Nginx edge + Apache бекенд (чистий розподіл)
- Переконайтесь, що Nginx слухає на
0.0.0.0:80та0.0.0.0:443. - Пересуньте Apache на
127.0.0.1:8080, відредагувавши/etc/apache2/ports.confта vhost’и. - В Nginx встановіть:
proxy_set_header Host $host;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;proxy_set_header X-Forwarded-Proto $scheme;
- Приберіть бекенд-HTTPS-редиректи, що використовують
%{HTTPS}, якщо їх не переписано під forwarded-proto. - Тестуйте бекенд напряму з curl до 127.0.0.1:8080, потім тестуйте через Nginx HTTPS.
- Безпечно перезавантажуйте сервіси:
nginx -tпотімsystemctl reload nginx;apachectl configtestпотімsystemctl reload apache2.
Чекліст 3: Тільки Apache (прямолінійно і нудно)
- Зупиніть і відключіть Nginx:
systemctl stop nginx,systemctl disable nginx. - Переконайтесь, що Apache слухає на 80/443 і має правильні vhost’и.
- Підтвердіть за
ss -ltnp, що Apache володіє портами. - Запустіть
apachectl -Sі підтвердіть, що очікуваний vhost є дефолтним для кожного порту.
Чекліст 4: Тільки Nginx (сучасний app backend)
- Зупиніть і відключіть Apache, якщо він не використовується.
- Проксіруйте на сервер додатка на окремому порті/сокеті.
- Тримайте примус редиректів на рівні Nginx лише.
- Налаштуйте логи так, щоб можна було налагоджувати помилки upstream без гадання.
Питання й відповіді (FAQ)
1) Чи можуть Apache і Nginx одночасно слухати на порті 80, якщо один привʼязується до IPv6, а інший до IPv4?
Інколи так, але ви купуєте дивний крайній випадок. Правила dual-stack binding можуть зробити це помилково працюючим до першого разу, коли все піде шкереберть. Оберіть одного власника для 80/443.
2) Який порт вибрати для бекенду Apache — 8080 чи 8000?
Використовуйте будь-який вільний високий порт, але будьте послідовні. 8080 — звичний вибір, що полегшує життя майбутнім людям. Привʼязуйте його до 127.0.0.1, якщо не потрібен віддалений доступ.
3) Чому мій додаток постійно редиректить на HTTP, хоча клієнти використовують HTTPS?
Бо бекенд бачить plain HTTP-зʼєднання від проксі і не знає, що клієнт використовував HTTPS. Виправте, пересилаючи X-Forwarded-Proto і налаштувавши додаток/фреймворк довіряти проксі-заголовкам.
4) Який найшвидший спосіб довести, що існує самопроксі петля?
Подивіться на proxy_pass і порівняйте з прослуховуваними сокетами. Якщо Nginx слухає :80 і проксірує на 127.0.0.1:80 — це петля. Також дивіться логи Nginx на наявність таймаутів upstream під час того, як upstream — «сам він».
5) Чому важливо вимикати дефолтний сайт Nginx?
Тому що вибір дефолтного vhost детермінований, але не завжди інтуїтивний у стресі. Залишений дефолтний сайт підвищує ймовірність того, що несподіване імʼя хоста або IP отримає не той контент.
6) Чи нормально проксірувати на hostname замість 127.0.0.1?
Може бути, але ризиковіше. Зміни DNS, split-horizon та /etc/hosts-хаки можуть перетворити «бекенд» на «фронтдор» знову. Для локальних бекендів віддавайте перевагу явному loopback.
7) Чи потрібно відкривати бекенд-порт в UFW?
Ні, якщо бекенд привʼязаний до 127.0.0.1. Тримайте його приватним. Якщо привʼяжете до 0.0.0.0, доведеться керувати правилами брандмауера і зрештою щось забудете.
8) Чому я бачу заголовки Apache, хоча Nginx спереду?
Тому що бекенд встановлює заголовок Server:, і Nginx за замовчуванням передає його далі. Якщо вам це важливо, ви можете приховати або переписати їх, але не плутайте заголовки з тим, хто володіє сокетом.
9) Що з HTTP/2, HTTP/3 і налаштуваннями TLS?
Ці речі — на кордоні. Якщо Nginx термінує TLS, налаштовуйте TLS і HTTP/2 там. Бекенд має бути простим: стабільний HTTP/1.1 через loopback — це легко і передбачувано для налагодження.
10) Як уникнути відновлення цієї проблеми після оновлень пакетів?
Вимикайте невикористовувані сервіси і контролюйте ідентичність, а не лише доступність. Підтверджуйте, що правильний сервер відповідає на правильному порту з правильними заголовками/сертифікатом. «Порт відкритий» — це занадто низька планка.
Висновок: що ви можете зробити сьогодні
Плутанина Apache vs Nginx на Ubuntu 24.04 зазвичай не є таємницею. Це два сервіси, що намагаються бути одним і тим самим на тих самих портах з неоднаковими уявленнями про схему й хост. Виправте це, прийнявши рішення і перевіривши базові, нудні примітиви: слухачі, upstream’и, заголовки, редиректи.
- Запустіть
ss -ltnpі запишіть, хто володіє 80/443. - Оберіть топологію: тільки Nginx, тільки Apache або Nginx edge + Apache бекенд.
- Якщо ви проксіруєте, зробіть upstream’и однозначними (127.0.0.1:8080) і пересилайте
X-Forwarded-Proto. - Приберіть дублювання логіки редиректів між шарами; забезпечте примус HTTPS в одному місці.
- Зафіксуйте це: вимкніть невикористовувані дефолтні сайти та сервіси, щоб оновлення не «допомогли» їх воскресити.