Difference between revisions of "OpenEMR with nginx and php-fpm"

From OpenEMR Project Wiki
(Created page with "drum roll.... (under construction)")
 
(Initial entry)
Line 1: Line 1:
drum roll....
The LAMP stack (Linux + Apache + MySQL + PHP) is very popular for powering OpenEMR. However is it possible to use Nginx?. OpenEMR may support Nginx, in fact, some OpenEMR sites are powered by Nginx as the reverse proxy with Apache. At least that is my understanding. A while back, after an update, my OpenEMR, FreeBSD/Apache/Mysql/PHP stack broke down. That is when I started looking for other solutions and came across Nginx. I liked it.
(under construction)
 
There are multiple ways to implement Nginx. However, this guide implements Nginx as the primary server instead of Apache. Please note that Apache and Nginx are not interchangeable. Hence this guide. For example, there is no directory-level configuration (.htaccess or IIS's Web.config files) in Nginx. All configuration must be done at the server level, and the server must be reloaded with each change.
 
Initially, this guide assumes you already have a working SSL enabled Nginx server with PHP7.2 and php-fpm is enabled and running. Later on, I may add sections on how to install the different components in a FreeBSD system, which is what I know. Hopefully, you already have a basic understanding of how to work with Nginx and how to debug it.
 
Please note that this set up is preliminary and new. There may be bugs in it. Use it at your own risk. We set up OpenEMR with SSL right from the start. Please post your comments at the community.open-emr.org forum.
 
In order for OpenEMR to work with Nginx, you have to configure the backend php-cgi. Nginx default install comes with three options, for implementation of the CGI module: 'fastcgi', 'scgi' and 'uwsgi'. The CGI module connects Nginx and the php-fpm (PHP Fast Program Manager). We use the FastCGI module. The "fastcgi_params" file can already be found in the default Nginx configuration directory, which in FreeBSD is located in /usr/local/etc/nginx/
 
This is my setup:
openemr-5.0.1_3
FreeBSD 11.1
nginx -1.14.0
Mysql-5.7
PHP-7.2 (includes the php-fpm server)
 
Important information regarding Nginx search instructions
^~  stop when found
~  case sensitive
~* case insensitive
placement and whitespaces are important
 
 
/usr/local/etc/nginx/nginx.conf
 
worker_processes  auto;
error_log  /var/log/nginx/error.log;
events {
worker_connections  1024;
}
# start the http block
http {
    include      mime.types;
    default_type  application/octet-stream;
 
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
access_log  /var/log/access.log  main;
 
    sendfile        on;
    #tcp_nopush    on;
 
    #keepalive_timeout  0;
server_names_hash_bucket_size 128;
    keepalive_timeout  65;
 
    gzip  off;
    upstream php {
server unix:/var/run/php-fpm.sock;
}
    index  index.html index.htm index.php;
 
# openemr.NET virtual host
# EHR site
  server {
    listen 80;
    listen [::]:80;
server_name  example.net www.example.net;
# redirect to ssl
return  301 https://$server_name$request_uri;
  }
 
#  openemr.NET https host
#  This is an ssl openemr site
  server {
listen 443 default_server ssl http2;
    listen  [::]:443 default_server ssl http2;
server_name  example.net www.example.net;
    root /usr/local/nginx/www;
 
## redirect www to nowww
    if ($host = 'www.example.net' ) {
rewrite  ^/(.*)$  https://example.net/$1  permanent;
 
    access_log /var/log/*/example.net_access_log main;
    error_log    /var/log/*/example.net_error_log notice;
 
# openemr specific SSL settings
# include openemr-ssl.conf; 
 
    ssl_certificate      /path/to/ssl.cert;
    ssl_certificate_key    /path/to/ssl.key;
 
# restrict/protect certain files
include globals.conf; 
 
# deny access to writable files/directories
location ~* ^/sites/*/(documents|edi|era) {
deny all;
}
# Pick one of the following two blockc, but not both:
# protect special files from outside openemer login, and restrict them to superAdmins only
location ~* ^/(admin|setup|acl_setup|acl_upgrade|sl_convert|sql_upgrade|gacl/setup|ippf_upgrade|sql_patch)\.php {
auth_basic "Restricted Access";
auth_basic_user_file /path/to/.htpasswd;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass php;
include fastcgi_params;
}
# Alternatively all access to these files can be denied
#location ~* ^/(admin|setup|acl_setup|acl_upgrade|sl_convert|sql_upgrade|gacl/setup|ippf_upgrade|sql_patch)\.php {
# deny all;
# return 404;
#}
 
location / {
# try as file ($uri), as directory ($uri/) if not found, send to index file
# no php is touched for static content
    try_files $uri $uri/ /index.php;
    }
    # redirect server error pages to the static page /50x.html
    error_page  500 502 503 504  /50x.html;
    location = /50x.html {
root  /usr/local/www/nginx-dist;
}
   
    # pass the PHP scripts to the FastCGI server listening on unix socket, in this case php-fpm
    location ~* \.php$ {
try_files $uri =404;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_index index.php;
    fastcgi_pass php;
        include fastcgi_params;
    }
# dynamic stuff goes to php-proxy
include php-proxy.conf;
}
} # end http block
 
 
 
This is the /usr/local/etc/nginx/globals.conf file:
 
# globals.conf configuration file.
# Designed to be included in any server {} block
# If this server only hosts openemr, this file can be merged with openemr.conf
 
# Stops the annoying error messages in the logs. robots are not allowed
location = /favicon.ico { log_not_found off; access_log off; }
location = /robots.txt  { log_not_found off; access_log off; }
 
# Deny all attempts to access hidden files such as .htaccess, .htpasswd, .DS_Store (Mac).
# Keep logging the requests to parse later (or to pass to firewall utilities such as fail2ban)
location ~ /\. { deny all; }
 
# protect or deny access to important server information and testing files
# alternatively, you can deny access to all files using {deny all; return 404;} or remove them
location ~* /(info|test)\.php$ {
auth_basic "Restricted Access";
auth_basic_user_file /path/to/.htpasswd;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass php;
include fastcgi_params;
}
 
# Not sure if openemr needs this. it comes from wordpress
location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
access_log off;
log_not_found off;
expires max;
}
 
## Deny certain Referers
if ( $http_referer ~* (babes|forsale|girl|jewelry|love|nudit|organic|poker|porn|sex|teen) ) {
        #return 404;
        return 403;
}
   
# Stop deep linking or hot linking
location /images/ {
  valid_referers none blocked www.example.com example.com;
  if ($invalid_referer) {
    return  403;
  }
}
 
 
/usr/local/etc/nginx/php-proxy.conf:
 
# php proxy block
# should have "cgi.fix_pathinfo = 0;" in php.ini
#
location ~ \.php$ {
    # php-fpm must be in same machine. Otherwise there is risk of hacking
    try_files $uri =404;
    fastcgi_index  index.php;
    fastcgi_pass unix:/var/run/php-fpm.sock;
    fastcgi_buffer_size 16k;
    fastcgi_buffers 4 16k;
    fastcgi_param SCRIPT_FILENAME $request_filename;
    include fastcgi_params;
    fastcgi_intercept_errors on;
    fastcgi_read_timeout 120;
}

Revision as of 22:43, 8 June 2018

The LAMP stack (Linux + Apache + MySQL + PHP) is very popular for powering OpenEMR. However is it possible to use Nginx?. OpenEMR may support Nginx, in fact, some OpenEMR sites are powered by Nginx as the reverse proxy with Apache. At least that is my understanding. A while back, after an update, my OpenEMR, FreeBSD/Apache/Mysql/PHP stack broke down. That is when I started looking for other solutions and came across Nginx. I liked it.

There are multiple ways to implement Nginx. However, this guide implements Nginx as the primary server instead of Apache. Please note that Apache and Nginx are not interchangeable. Hence this guide. For example, there is no directory-level configuration (.htaccess or IIS's Web.config files) in Nginx. All configuration must be done at the server level, and the server must be reloaded with each change.

Initially, this guide assumes you already have a working SSL enabled Nginx server with PHP7.2 and php-fpm is enabled and running. Later on, I may add sections on how to install the different components in a FreeBSD system, which is what I know. Hopefully, you already have a basic understanding of how to work with Nginx and how to debug it.

Please note that this set up is preliminary and new. There may be bugs in it. Use it at your own risk. We set up OpenEMR with SSL right from the start. Please post your comments at the community.open-emr.org forum.

In order for OpenEMR to work with Nginx, you have to configure the backend php-cgi. Nginx default install comes with three options, for implementation of the CGI module: 'fastcgi', 'scgi' and 'uwsgi'. The CGI module connects Nginx and the php-fpm (PHP Fast Program Manager). We use the FastCGI module. The "fastcgi_params" file can already be found in the default Nginx configuration directory, which in FreeBSD is located in /usr/local/etc/nginx/

This is my setup: openemr-5.0.1_3 FreeBSD 11.1 nginx -1.14.0 Mysql-5.7 PHP-7.2 (includes the php-fpm server)


Important information regarding Nginx search instructions

^~  stop when found
~  case sensitive
~* case insensitive
placement and whitespaces are important


/usr/local/etc/nginx/nginx.conf

worker_processes auto; error_log /var/log/nginx/error.log; events { worker_connections 1024; }

  1. start the http block

http {

   include       mime.types;
   default_type  application/octet-stream;
   log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                     '$status $body_bytes_sent "$http_referer" '
                     '"$http_user_agent" "$http_x_forwarded_for"';

access_log /var/log/access.log main;

   sendfile        on;
   #tcp_nopush     on;
   #keepalive_timeout  0;

server_names_hash_bucket_size 128;

   keepalive_timeout  65;
   gzip  off;
   upstream php { 

server unix:/var/run/php-fpm.sock; }

   index  index.html index.htm index.php;
  1. openemr.NET virtual host
  2. EHR site
 server {
   listen 80;
   listen [::]:80;

server_name example.net www.example.net; # redirect to ssl return 301 https://$server_name$request_uri;

 }
  1. openemr.NET https host
  2. This is an ssl openemr site
 server {

listen 443 default_server ssl http2;

   listen  [::]:443 default_server ssl http2;

server_name example.net www.example.net;

   root /usr/local/nginx/www;

## redirect www to nowww

   if ($host = 'www.example.net' ) { 

rewrite ^/(.*)$ https://example.net/$1 permanent; }

   access_log /var/log/*/example.net_access_log main;
   error_log    /var/log/*/example.net_error_log notice;

# openemr specific SSL settings

  1. include openemr-ssl.conf;
   ssl_certificate      /path/to/ssl.cert;
   ssl_certificate_key    /path/to/ssl.key;

# restrict/protect certain files include globals.conf;

# deny access to writable files/directories location ~* ^/sites/*/(documents|edi|era) { deny all; }

# Pick one of the following two blockc, but not both: # protect special files from outside openemer login, and restrict them to superAdmins only location ~* ^/(admin|setup|acl_setup|acl_upgrade|sl_convert|sql_upgrade|gacl/setup|ippf_upgrade|sql_patch)\.php { auth_basic "Restricted Access"; auth_basic_user_file /path/to/.htpasswd; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_pass php; include fastcgi_params; }

# Alternatively all access to these files can be denied #location ~* ^/(admin|setup|acl_setup|acl_upgrade|sl_convert|sql_upgrade|gacl/setup|ippf_upgrade|sql_patch)\.php { # deny all; # return 404; #}

location / { # try as file ($uri), as directory ($uri/) if not found, send to index file # no php is touched for static content

   	try_files $uri $uri/ /index.php;
   }	
   # redirect server error pages to the static page /50x.html
   error_page   500 502 503 504  /50x.html;
   location = /50x.html { 

root /usr/local/www/nginx-dist; }

   # pass the PHP scripts to the FastCGI server listening on unix socket, in this case php-fpm
   location ~* \.php$ {

try_files $uri =404;

   	fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
   	fastcgi_index index.php;
   	fastcgi_pass php;
       include fastcgi_params; 
   }

# dynamic stuff goes to php-proxy include php-proxy.conf; } } # end http block


This is the /usr/local/etc/nginx/globals.conf file:

  1. globals.conf configuration file.
  2. Designed to be included in any server {} block
  3. If this server only hosts openemr, this file can be merged with openemr.conf
  1. Stops the annoying error messages in the logs. robots are not allowed

location = /favicon.ico { log_not_found off; access_log off; } location = /robots.txt { log_not_found off; access_log off; }

  1. Deny all attempts to access hidden files such as .htaccess, .htpasswd, .DS_Store (Mac).
  2. Keep logging the requests to parse later (or to pass to firewall utilities such as fail2ban)

location ~ /\. { deny all; }

  1. protect or deny access to important server information and testing files
  2. alternatively, you can deny access to all files using {deny all; return 404;} or remove them

location ~* /(info|test)\.php$ { auth_basic "Restricted Access"; auth_basic_user_file /path/to/.htpasswd; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_pass php; include fastcgi_params; }

  1. Not sure if openemr needs this. it comes from wordpress

location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ { access_log off; log_not_found off; expires max; }

    1. Deny certain Referers

if ( $http_referer ~* (babes|forsale|girl|jewelry|love|nudit|organic|poker|porn|sex|teen) ) {

        #return 404;
        return 403;

}

  1. Stop deep linking or hot linking

location /images/ {

 valid_referers none blocked www.example.com example.com;
 if ($invalid_referer) {
    return   403;
  }

}


/usr/local/etc/nginx/php-proxy.conf:

  1. php proxy block
  2. should have "cgi.fix_pathinfo = 0;" in php.ini

location ~ \.php$ {

   # php-fpm must be in same machine. Otherwise there is risk of hacking
   try_files $uri =404;
   fastcgi_index  index.php;
   fastcgi_pass unix:/var/run/php-fpm.sock;
   fastcgi_buffer_size 16k;
   fastcgi_buffers 4 16k;
   fastcgi_param SCRIPT_FILENAME $request_filename;
   include fastcgi_params;
   fastcgi_intercept_errors on;
   fastcgi_read_timeout 120;

}