How to activate Nginx logging in Linux Azure App Service

This post summarizes how to activate and configure the native Nginx logging, storing the log files in an external storage and colleting the log coming from scaled-out instances of a Linux Azure App Service, hosting a PHP application.



The default PHP 8.4 configuration of Azure App Service for Linux uses Nginx but the http logging is explicitly disabled in the configuration file /etc/nginx/nginx.conf by the command access_log off;. To re-activate it, we need to switch the access_log directive to on and reload Nginx configuration with service nginx reload command.

The default configuration does not set-up the logging, so we need to create a custom configuration file to do that. The custom logs configuration should be placed in a dedicated file under /etc/nginx/sites-enabled/. Every files in this folder will be automatically loaded by Nginx at startup, thanks to the include /etc/nginx/sites-enabled/*; instruction in the /etc/nginx/nginx.conf.

Because every modification and every file added outside the /home folder are lost at reboot, we need to create a script under /home to setup everything at startup. The name of the script to be executed must be specified in the App Service configuration in Azure Portal.

In our scenario, the script must:

  • activate the logging in the main nginx configuration file
  • copy the custom log configuration file
  • reload Nginx configuration

File /home/mysetup/setup.sh :

sed -i 's/access_log off;/access_log on;/g' /etc/nginx/nginx.conf
cp /home/mysetup/mylog.conf /etc/nginx/sites-enabled/
service nginx reload

Scale out warnings !

When we do SSH into the App Service, we connect to first instance. If we just have one instance, we can edit files, like setup.sh, mylog.conf, run setup.sh, reload nginx configuration, etc. directly from ssh and everything works as expected.But if we have scaled out the App Service to multiple instances, only the first instance will be affected by our changes. To apply to all instances, we must restart the whole App Service from Azure Portal. This way, every instance will restart, re-executing the startup script.

Another problem with scaled out App Service, is the location of the log files. If you place them somewhere under /home, every instance will have its own log file(s). This is not what normally we need. Instead we need a single log file that collets all the rows from all the nginx instances. To do what, we need to place the log file in an external storage, mounting the same storage on every instance. Steps:

  1. Create an Azure Storage Account or use an existing one
  2. Create a dedicated File Share, e.g. nginxlogs
  3. Mount the File Share in the App Service configuration, e.g. /home/nginxlogs
  4. Configure the mylog.conf to write to that folder (the example below already does that).

Storing log files in an external storage is also a convenient way to avoid filling the local disk of the App Service and to simplify access to the log files.


mylog.conf example

This example shows some interesting features:

  • multiple output log files.
  • daily log files.
  • just for testing, different log files every minute.
  • both text and ‘json lines’ log files.
  • use of some particular variables, like $http_x_forwarded_for and $time_iso8601.
  • intercepting a custom header.
  • assigning a default value to a variable, to avoid ab empty field in the log file.
map $sent_http_X_WP_Cache $log_cached { default $sent_http_X_WP_Cache; '' 0; }

map $time_iso8601 $log_year   { default '0000'; "~^(\d{4})-(\d{2})-(\d{2})" $1; }
map $time_iso8601 $log_month  { default '00';   "~^(\d{4})-(\d{2})-(\d{2})" $2; }
map $time_iso8601 $log_day    { default '00';   "~^(\d{4})-(\d{2})-(\d{2})" $3; }
map $time_iso8601 $log_hour   { default '00';   "~^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})" $4; }
map $time_iso8601 $log_minute { default '00';   "~^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})" $5; }

log_format logtxt '$time_iso8601 $remote_addr $request_time $hostname "$request" '
                  '$status $body_bytes_sent "$http_referer" '
                  '"$http_user_agent" "$http_x_forwarded_for" "$sent_http_set_cookie" ';

log_format logjson escape=json
'{ '
  '"DT": "$time_iso8601", '
  '"RemoteAddress": "$remote_addr", '
  '"RequestTime": "$request_time", '
  '"HostName": "$hostname", '
  '"Request": "$request", '
  '"Status": "$status", '
  '"BytesReceived": "$request_length", '
  '"BytesSent": "$bytes_sent", '
  '"X-Forwarded-For": "$http_x_forwarded_for", '
  '"Cached": "$log_cached", '
  '"UserAgent": "$http_user_agent", '
  '"HttpReferer": "$http_referer", '
  '"SetCookies": "$sent_http_set_cookie" '
'}';

access_log /home/nginxlogs/http_${log_year}${log_month}${log_day}.json logjson;
access_log /home/nginxlogs/http_${log_year}${log_month}${log_day}.log logtxt;
access_log /home/nginxlogs/http_${log_year}${log_month}${log_day}_${log_hour}${log_minute}.log logtxt;  


Json-lines log output example:

{ "DT": "2025-09-06T14:58:21+00:00", "RemoteAddress": "169.254.130.1", "RequestTime": "0.006", "HostName": "cbb0e5xxxxxxxx", "Request": "GET /page2.php HTTP/1.1", "Status": "200", "BytesReceived": "1507", "BytesSent": "509", "X-Forwarded-For": "87.xxx.xxx.xxx:55159", "Cached": "1", "UserAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36", "HttpReferer": "https://xxxxxxxxxxxxxxxxxxxxx.azurewebsites.net/page1.php", "SetCookies": "userid=123456; expires=Thu, 01 Jan 2026 01:01:01 GMT; Max-Age=10058561; path=/" }
{ "DT": "2025-09-06T15:14:32+00:00", "RemoteAddress": "169.254.130.1", "RequestTime": "0.125", "HostName": "cbb0e5xxxxxxxx", "Request": "GET /page2.php?a=f2123 HTTP/1.1", "Status": "200", "BytesReceived": "828", "BytesSent": "540", "X-Forwarded-For": "87.xxx.xxx.xxx:62903", "Cached": "0", "UserAgent": "curl/8.14.1", "HttpReferer": "", "SetCookies": "PHPSESSID=d329528e9ce7bf468a43642880075af7; path=/, userid=123456; expires=Thu, 01 Jan 2026 01:01:01 GMT; Max-Age=10057590; path=/" }
{ "DT": "2025-09-06T15:14:34+00:00", "RemoteAddress": "169.254.130.1", "RequestTime": "0.802", "HostName": "cbb0e5xxxxxxxx", "Request": "GET /page2.php?a=f2123 HTTP/1.1", "Status": "200", "BytesReceived": "828", "BytesSent": "540", "X-Forwarded-For": "87.xxx.xxx.xxx:62906", "Cached": "0", "UserAgent": "curl/8.14.1", "HttpReferer": "", "SetCookies": "PHPSESSID=72727ef42b052115b067b5937a655807; path=/, userid=123456; expires=Thu, 01 Jan 2026 01:01:01 GMT; Max-Age=10057588; path=/" }