icomHOST

Wszystko o domenach i hostingach

Czym są limity na wykonywanie skryptów PHP

Czym są limity na wykonywanie skryptów PHP

Limity wykonywania skryptów PHP to zasady i progi zaszyte w konfiguracji środowiska uruchomieniowego, które pilnują, aby pojedynczy proces nie pochłaniał nadmiernych zasobów, nie blokował innych usług i nie pogarszał jakości obsługi ruchu. W świecie serwerów i usług współdzielonych mają one funkcję porządkującą: pomagają utrzymać równowagę między stabilnością, wydajność a kosztami. Dobrze dobrane i świadomie zarządzane, stają się sprzymierzeńcem twórców aplikacji i administratorów. Ustawione bez zrozumienia — potrafią generować trudne do diagnozowania błędy, przestoje i frustrację użytkowników.

Czym są limity wykonywania skryptów PHP i po co istnieją

Pod nazwą limitów kryją się z jednej strony parametry konfiguracji PHP (np. czas maksymalnego wykonania, limit pamięć, ograniczenia rozmiaru wejścia), z drugiej — reguły serwera www, menedżera procesów (np. PHP-FPM) czy warstw pośrednich (reverse proxy, firewall aplikacyjny). Ich wspólnym celem jest ochrona dostępności i przewidywalności zasobów. Dzięki temu nawet przy szczycie obciążenia strona pozostaje responsywna, a pojedynczy błąd programistyczny nie przełoży się na lawinę skutków ubocznych.

Limity zapobiegają też niezamierzonym kosztom. W środowiskach chmurowych i kontenerowych czas, CPU, I/O czy pamięć są bezpośrednio przeliczane na pieniądze. W hostingu współdzielonym — na skalę zadowolenia setek innych klientów. Brak limitów to ryzyko wycieków pamięci, zapętleń, pracujących w tle skryptów, które nigdy nie kończą zadania i latami zużywają zasoby.

Kluczowe rodzaje limitów w PHP i stosie serwerowym

Parametry PHP (php.ini, .user.ini, .htaccess, ini_set)

  • max_execution_time — maksymalny czas trwania pojedynczego żądania PHP (w sekundach). W trybie CLI często 0, co znaczy bez limitu, ale web SAPI zwykle ma limit. Gdy zostanie przekroczony, pojawia się komunikat Maximum execution time exceeded. To klasyczny timeout po stronie interpretera.
  • memory_limit — maksymalna ilość pamięci, jaką może zaalokować proces. Gdy skrypt ją przekroczy, zobaczymy Allowed memory size exhausted. Uwaga: to limit na proces/żądanie, a nie na całą usługę — wiele równoległych procesów kumuluje realny użytek RAM.
  • max_input_time — czas na przetworzenie danych wejściowych (POST, GET, pliki) zanim trafią do skryptu.
  • post_max_size — maksymalny rozmiar danych przesyłanych metodą POST (łącznie z polami formularzy i plikami w trybie multipart).
  • upload_max_filesize — maksymalny rozmiar pojedynczego pliku przesyłanego przez PHP. Musi pozostawać spójny z post_max_size.
  • max_file_uploads — liczba plików możliwych do wysłania w jednym żądaniu.
  • max_input_vars — liczba zmiennych wejściowych (np. pól formularzy). Zbyt niska wartość może powodować tajemnicze obcinanie danych (np. duże menu w CMS).
  • default_socket_timeout — czas oczekiwania funkcji sieciowych (np. połączeń HTTP, SMTP) wykonywanych ze skryptu PHP. Często pomijany, a krytyczny dla integracji z API.
  • realpath_cache_size i realpath_cache_ttl — wpływają na zużycie pamięci i szybkość rozwiązywania ścieżek plików.
  • Ustawienia OPcache (np. opcache.memory_consumption, opcache.max_accelerated_files) — nie są limitami wykonania per se, ale wprost determinują, czy kod będzie kompilowany każdorazowo, czy serwowany z pamięci, co zmienia profil zużycia zasobów i stabilność czasu odpowiedzi.

Ograniczenia menedżera procesów i serwera www

  • PHP-FPM: pm (dynamic/static/ondemand), pm.max_children (maksymalna liczba równoległych procesów), pm.max_requests (ochrona przed wyciekami pamięci), request_terminate_timeout (twardy limit czasu na żądanie), request_slowlog_timeout (logowanie wolnych żądań). To one decydują, ile równoległych skryptów obsłuży serwer i jak rozłoży się presja na RAM i CPU.
  • Serwer NGINX/Apache: fastcgi_read_timeout, proxy_read_timeout, LimitRequestBody, Timeout, KeepAlive (Apache). Te parametry nakładają limity przed wejściem do PHP lub w trakcie wymiany danych z backendem.
  • Reverse proxy/CDN/WAF: dodatkowe limity czasu i wielkości odpowiedzi mogą przerywać długie transakcje, nawet jeśli PHP jest jeszcze w trakcie pracy.

Limity systemowe i kontenerowe

  • ulimit (nproc, nofile) — ograniczenia liczby procesów i otwartych plików.
  • Kontenery/cgroups — limity CPU shares, memory limit, I/O throttling, które „ściskają” procesy PHP niezależnie od ustawień php.ini.
  • Limity dyskowe (quota, inode), które mogą nagle zablokować sesje, cache lub uploady, skutkując błędami aplikacji.

Jak i gdzie konfigurować limity

Źródłem prawdy jest zwykle php.ini, ale w środowiskach współdzielonych nie zawsze mamy do niego dostęp. Wówczas można użyć .user.ini (PHP w trybie CGI/FPM je respektuje) lub dyrektyw w .htaccess (mod_php). Część wartości da się przestawić z poziomu kodu przez ini_set, lecz nie wszystkie (parametry oznaczone jako PHP_INI_SYSTEM wymagają uprawnień administracyjnych). W panelach hostingowych często znajdziemy interfejs do modyfikacji podstawowych limitów per domena lub per wersja PHP.

Ważna różnica: max_execution_time odnosi się do czasu pracy skryptu, a fastcgi_read_timeout do cierpliwości serwera www w oczekiwaniu na odpowiedź od FPM. Jeżeli skrypt potrzebuje 120 sekund, a fastcgi_read_timeout wynosi 60, klient dostanie błąd 504 Gateway Timeout mimo że PHP wciąż działa. Z kolei request_terminate_timeout w FPM położy proces niezależnie od max_execution_time, bo to wyższa „instancja” decyzyjna.

W trybie CLI (zadania cron, migracje) domyślny max_execution_time bywa ustawiony na 0 (bez limitu), ale memory_limit nadal obowiązuje. To zachęca do przenoszenia ciężkich zadań do kolejek i workerów uruchamianych poza cyklem żądań webowych.

Wpływ limitów na aplikacje i typowe symptomy

  • Masowe importy (np. produktów) zakończą się błędem Maximum execution time exceeded lub Allowed memory size exhausted, jeśli zadanie nie jest dzielone na porcje i działa na dużych tablicach w pamięci.
  • Duże formularze konfiguracji potrafią „obcinać” dane, gdy max_input_vars jest zbyt niskie.
  • Upload wideo lub dużych zdjęć zakończy się 413 Request Entity Too Large bądź 500/504, jeśli post_max_size, upload_max_filesize lub bufory reverse proxy nie są dostateczne.
  • Integracje z zewnętrznymi API zamilkną po cichu, jeśli default_socket_timeout jest zbyt niski względem SLA usług zewnętrznych, albo nie ma obsługi retry/backoff.
  • W CMS-ach (np. sklepy, blogi) brak cache i wysoki ruch ujawnia limity FPM: kolejka żądań rośnie, a średni TTFB skacze; wtedy nie pomaga samo zwiększanie max_execution_time — potrzebne jest podejście architektoniczne (cache, skalowanie poziome).

Warto pamiętać, że błędy 502/504 nie zawsze pochodzą od PHP. Niejednokrotnie to reverse proxy lub load balancer, który stracił cierpliwość, podczas gdy FPM pracuje dalej. Dlatego diagnostyka musi obejmować więcej niż jeden poziom logów.

Projektowanie pod limity: praktyki programistyczne

  • Strumieniowanie i przetwarzanie porcjami: zamiast wczytywać cały plik CSV do pamięci, czytaj i zapisuj rekordy w chunkach, z kontrolą pamięci i checkpointami.
  • Asynchroniczność i kolejki: ciężkie operacje (generowanie raportów, konwersje obrazów) deleguj do workerów poza cyklem HTTP. Cron lub system kolejek przejmie długie zadania, a użytkownik dostaje szybką odpowiedź.
  • Optymalizacja zapytań: indeksy, ograniczanie SELECT *, paginacja. Każde niepotrzebne skanowanie tabel to nie tylko czas, ale i pamięć PHP zużywana na serializację wyników.
  • Cache wielopoziomowy: OPcache w PHP to fundament, ale do danych aplikacyjnych użyj pamięci współdzielonej (redis/memcached) albo plikowego cache’u z polityką invalidacji. Mniej pracy dla PHP to mniejsze ciśnienie na limity.
  • Kontrola alokacji: unikaj mnożenia wielkich struktur (np. mapowania całej bazy do tablicy). Korzystaj z generatorów, kursora bazy lub iteratorów.
  • Wydzielanie ścieżek krytycznych: API, które musi reagować w 100 ms, trzymaj z dala od raportów generowanych minutami. Osobne pule FPM, osobne zasoby i niezależne limity.
  • Idempotencja i retry: długie procesy muszą być przerywalne i wznawialne. To ułatwia życie przy agresywnych limitach czasu.

Dobór limitów do profilu ruchu i modelu hostingu

Na serwerze współdzielonym podniesienie memory_limit do 1G mija się z celem — wąskim gardłem będzie i tak liczba procesów FPM i globalny budżet RAM dla całej maszyny. Na VPS lub dedyku możemy świadomie żonglować pm.max_children, pamiętając, że proces o memory_limit 256M pomnożony przez 20 równoległych żądań daje potencjalnie ponad 5 GB zużycia, do czego dochodzi cache i system plików.

W architekturze mikroserwisowej korzystne bywa wydzielenie profilów: pula o małym memory_limit i krótkim max_execution_time dla endpointów API oraz osobna pula do zadań tła z dłuższymi limitami i innymi priorytetami CPU. Dzięki temu wrażliwe komponenty pozostają responsywne nawet przy naporze ciężkich zadań.

Podnoszenie limitów a bezpieczeństwo

Im wyższe limity, tym większe ryzyko eskalacji skutków błędów i nadużyć. Długi czas wykonania to więcej okazji na okupowanie procesów przez wrogie żądania; wysoki post_max_size ułatwia ataki pamięciowe lub napełnienie dysku. Odpowiedzialne podnoszenie limitów idzie w parze z mechanizmami ochrony: rate limiting, WAF, limity przepustowości, narzędzia anty-botowe oraz sensowna obserwowalność.

Warto wdrożyć twarde bezpieczniki wyżej w stosie: request_terminate_timeout w FPM, ograniczenia reverse proxy oraz politykę automatycznego ubijania procesów, które przekroczyły budżet czasu lub pamięci. W środowiskach kontenerowych pomocne są limity cgroups — gwarantują, że nawet pomyłkowo ustawiony memory_limit w PHP nie zje całego węzła.

Monitorowanie i diagnostyka problemów z limitami

  • phpinfo i logi błędów PHP — pierwsze źródła prawdy o aktualnych wartościach i incydentach Allowed memory size / Maximum execution time.
  • FPM slowlog — zapisuje stacktrace żądań przekraczających request_slowlog_timeout. Niezastąpiony w śledzeniu wąskich gardeł.
  • Logi NGINX/Apache — korelacja kodów 502/504, czasów upstream, rozmiarów ciała żądania.
  • Metryki systemowe — CPU steal, iowait, ciśnienie pamięci. Często to one stoją za losowymi timeoutami.
  • Profilery aplikacyjne — identyfikują najdroższe transakcje, hot path, n+1 queries, nadmierne alokacje.
  • Testy obciążeniowe — pozwalają przetestować skutki zmian limitów przed wdrożeniem na produkcji.

Przykładowe zestawy ustawień dla typowych scenariuszy

Mały blog/CMS o umiarkowanym ruchu

  • memory_limit: 128–256M
  • max_execution_time: 30–60s
  • post_max_size i upload_max_filesize: 16–64M (spójne)
  • max_input_vars: 2000–3000 (duże menu, wtyczki)
  • OPcache: włączony, pamięć 128–256M
  • FPM: pm.dynamic, pm.max_children zgodnie z RAM (np. 10–20 przy 2–4 GB)

Sklep internetowy (większa baza, sporadyczne importy)

  • memory_limit: 256–512M
  • max_execution_time: 60–120s (web), 0–300s (CLI)
  • post_max_size i upload_max_filesize: 64–256M
  • default_socket_timeout: 30–60s z retry po stronie klienta
  • FPM: osobna pula dla admin/cron z dłuższymi limitami i mniejszym priorytetem
  • Cache: page/fragment + obiektowy (redis), intensywnie używany

API o niskich opóźnieniach

  • memory_limit: 128–256M (niskie, by upchnąć więcej procesów)
  • max_execution_time: 10–30s (krótkie SLA)
  • default_socket_timeout: 3–10s i agresywne timeouty do backendów
  • FPM: osobna pula, pm.max_children dopasowany do CPU i RAM
  • Reverse proxy: krótkie time‑outy, circuit breakers, cache odpowiedzi, gzip/br

Worker/kolejka do ciężkich zadań

  • CLI, max_execution_time: 0 (bez limitu), ale kontrolowane czasy trwania zadań po stronie kodu
  • memory_limit: 512M–1G (w zależności od natury zadań)
  • Automatyczne restartowanie po N zadaniach (pm.max_requests analogicznie w FPM) w celu walki z wyciekami
  • Backpressure: ograniczanie konkurecji, aby nie zabić I/O i bazy danych

Najczęstsze mity i pułapki

  • „Podniosę memory_limit i problem zniknie” — zwykle to tylko odroczenie diagnozy. Jeśli alokacje rosną bez umiaru, potrzebna jest zmiana algorytmu lub strumieniowanie.
  • „max_execution_time 0 rozwiąże time‑outy” — w web SAPI i tak uderzysz w fastcgi_read_timeout lub politykę reverse proxy. Długie operacje przenieś do tła.
  • „upload_max_filesize wystarczy zwiększyć” — musi być spójny z post_max_size i buforami serwera/reverse proxy. Inaczej klient nigdy nie dotrze do PHP.
  • „OPcache przyspiesza wszystko bez kosztów” — wymaga pamięci i właściwych wartości opcache.memory_consumption oraz opcache.max_accelerated_files; przepełniony cache szkodzi.
  • „Im więcej procesów FPM, tym lepiej” — do czasu, aż zabraknie RAM i zacznie się swapping. Wtedy każdy request robi się wolniejszy.

Strategie skalowania w granicach limitów

  • Skalowanie pionowe (więcej CPU/RAM) — proste, ale kosztowne; ryzyko „jednego wielkiego węzła”.
  • Skalowanie poziome — więcej instancji FPM za load balancerem, krótsze limity i agresywniejszy cache.
  • Separacja obciążeń — dedykowane pule dla API, admina, kolejek; różne limity i priorytety.
  • Edge caching i CDN — mniej żądań do PHP, stabilniejsze czasy odpowiedzi, relatywnie niższe ryzyko przekraczania limitów.
  • Offload zadań CPU-intensywnych — zewnętrzne usługi przetwarzania obrazów/wideo lub narzędzia poza PHP.

Checklist dla administratora i dewelopera

  • Sprawdź realne użycie RAM per proces i sumaryczne piki pod obciążeniem.
  • Zgraj max_execution_time z fastcgi_read_timeout i request_terminate_timeout.
  • Ustal spójne post_max_size i upload_max_filesize, pamiętając o buforach reverse proxy.
  • Włącz i prawidłowo skonfiguruj OPcache; monitoruj wskaźnik hit rate i pełność cache.
  • Wprowadź FPM slowlog i alerty dla wolnych żądań oraz błędów memory/time‑out.
  • Wyodrębnij długie zadania do kolejek/CLI; web trzymać szybki i przewidywalny.
  • Testuj zmiany limitów narzędziami do obciążenia przed wdrożeniem.
  • Dokumentuj różnice między środowiskami (dev/stage/prod), aby uniknąć „niespodzianek”.

Różnice między środowiskami i migracje

Dystrybucje Linuksa i obrazy kontenerowe potrafią mieć inne domyślne wartości php.ini, inny sposób ładowania rozszerzeń i różne parametry kompilacji. Migracja aplikacji bez inwentaryzacji limitów często kończy się incydentami: nagłym skokiem TTFB, 502/504 lub problemami z uploadem. Dokumentuj i przenoś: php.ini, konfiguracje FPM, zasady reverse proxy, uprawnienia i limity systemowe.

Wersja PHP ma znaczenie: zmiany w zarządzaniu pamięcią, garbage collector, JIT (w nowszych wydaniach) oraz zachowanie rozszerzeń mogą modyfikować profil zużycia zasobów. Po aktualizacji warto weryfikować nie tylko poprawność funkcjonalną, ale i charakterystykę pracy pod obciążeniem — czasem lepiej „ściągnąć” limity po optymalizacji, by zyskać przepustowość.

Przykłady diagnoz „krok po kroku”

Import, który zawsze przerywa się po ~30 sekundach

  • Sprawdź logi PHP — Maximum execution time? Jeśli tak, podnieś max_execution_time lub przenieś zadanie do CLI/queue.
  • Jeśli brak błędu w logu PHP, a klient widzi 504, sprawdź fastcgi_read_timeout/proxy_read_timeout. Zgraj je z max_execution_time.
  • Zweryfikuj, czy import można porcjować i checkpointować; analizuj pamięć (czy nie rośnie liniowo z liczbą rekordów).

Losowe błędy Allowed memory size exhausted

  • Oceń alokacje: loguj memory_get_usage na krytycznych etapach.
  • Wyłącz zbędne pluginy/feature flagi, które tworzą kopie dużych struktur.
  • Ustaw pm.max_requests w FPM, aby okresowo restartować procesy i czyścić ewentualne wycieki.
  • Jeśli to import/eksport, wdroż strumieniowanie danych i batch size.

Duży upload zrywa się natychmiast

  • Porównaj post_max_size i upload_max_filesize; większy z nich nie zadziała bez zwiększenia drugiego.
  • Sprawdź LimitRequestBody (Apache) lub client_max_body_size (NGINX) oraz limity na poziomie WAF/CDN.
  • Rozważ uploady dzielone na fragmenty i wznawialne, aby nie blokować długimi strumieniami pojedynczych procesów PHP.

Wzorce architektoniczne, które „lubią” limity

  • Projektuj pod awarie: jeśli coś może się nie udać, niech będzie łatwe do powtórzenia bez skutków ubocznych.
  • Preferuj krótkie, stateless żądania w warstwie HTTP. Długie przepływy przenieś do workerów.
  • Dbaj o parametryzację limitów per środowisko i per usługa. Jeden rozmiar nie pasuje do wszystkich.
  • Używaj metryk i budżetów wydajnościowych jako kontraktów: średnie i p95 czasów odpowiedzi, zużycie pamięci na żądanie, liczba procesów na rdzeń CPU.

Podsumowanie praktyczne

Limity wykonywania skryptów w PHP nie są przeszkodą, lecz narzędziem do budowy przewidywalnych, szybrych i odpornych systemów. Dają ramy, w których łatwiej zarządzać kosztami, jakością i ryzykiem. Wymagają jednak świadomego doboru i korelacji na wszystkich poziomach: od php.ini, przez PHP-FPM, po serwer www, reverse proxy i system operacyjny. Najlepsze efekty przynosi połączenie odpowiednich wartości konfiguracyjnych z dobrym rzemiosłem programistycznym: porcjowaniem pracy, cache’owaniem, profilowaniem i testami obciążeniowymi. A kiedy aplikacja rośnie, warto sięgnąć po izolację pul, separację ról i skalowanie — tak, aby granice narzucone przez limity stały się sprzymierzeńcem, a nie hamulcem rozwoju.