This will just be a quick post. I recently started to evaluate using Koken to self-host some of my photography work and I was having a lot of mysterious issues. My server is configured running nginx and all domains are 100% SSL, meaning there is no non-SSL access to a site. Koken is a PHP application built on the CodeIgniter framework, so there is also php-fpm in the mix too.
While Koken looks really nice, nice enough for me to spend time trying to resolve my issues, the support is pretty poor. There are hundreds of posts related to the following issues, which I have resolved with one configuration item in my nginx config. I hope this might solve some problems for a lot of other people since development appears very slow since it was sold. I believe the issues above are isolated to people running Koken on nginx with SSL only access.
- SSL Mixed Content Warnings
- Unable to Log in, despite knowing the correct password
- Settings do not save
- Themes will not apply
- Cannot contact API
As it turns out, /api.php is the primary way the application talks to itself, and /app/site/site.php is the primary controller for things. After a bunch of logging and debugging I noticed that site.php was making a lot of calls to api.php using a non ssl uri. For whatever reason these calls never made it to my 301 redirects and would just silently fail resulting in a bunch of strange issues where the site appears to “mostly work”, but a lot of core functionality seems broken.
Upon realizing that the application was calling the api from http instead of https I was hoping I could resolve the issue without modifying the source code. I did not want to have fiddled with the source such that any time I update it, I would have to remember to patch the files. After a bit of digging I found that Koken determines if a site is running SSL using the following code snippet from /app/site/Koken.php :
if ($options['echo']) { if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') { $prefix = 'https:'; } else { $prefix = 'http:'; } $url = $prefix . '//' . $_SERVER['HTTP_HOST'] . $url; echo $url; }
There are a number of places throughout the code which make reference to the $_SERVER[‘HTTPS’] variable, some deal with it properly, some do not. The above snippet is just one example, to correct the issue in the code itself you would have modify 5 or 6 files. While I feel the Koken developers can fix this on their end, or at least document the configuration change needed to correct it, the fix is quite simple.
In your fastcgi.conf, or the site configuration file, or wherever you store it, add the following line:
fastcgi_param HTTPS 'on';
This will properly set the $_SERVER[‘HTTPS’] variable to something that will work with how Koken expects to reference it (like Apache), and everything will work fine.
The relevant sections of my entire working koken.conf file for nginx looks like this:
server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name kokensite; add_header Strict-Transport-Security "max-age=63072000"; #add_header X-Frame-Options DENY; ssl on; ssl_certificate /path/to/cert/fullchain.pem; ssl_certificate_key /path/to/cert/privkey.pem; ssl_stapling on; ssl_stapling_verify on; ssl_trusted_certificate /path/to/cert/fullchain.pem; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_prefer_server_ciphers on; ssl_ecdh_curve secp384r1; ssl_session_cache shared:SSL:100m; ssl_session_timeout 24h; ssl_session_tickets on; ssl_session_ticket_key /path/to/ticket.key; ssl_dhparam /path/to/dhparam4.pem; ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK'; resolver 8.8.8.8 8.8.4.4 208.67.222.222 208.67.220.220 216.146.35.35 216.146.36.36 valid=300s; resolver_timeout 3s; error_log /path/to/koken/log/koken.error.log; # access_log off; access_log /path/to/koken/log/koken.access.log; gzip on; gzip_disable msie6; gzip_static on; gzip_comp_level 9; gzip_proxied any; gzip_min_length 1100; gzip_buffers 16 8k; gzip_types text/plain text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript; expires 0; root /path/to/koken/html; index index.php index.html; # Do not allow access to hidden files. location ~ /\. { deny all; } # pass all other PHP requests to main backend location ~ \.php$ { try_files $uri =404; include /etc/nginx/fastcgi.conf; fastcgi_param HTTPS 'on'; fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_index index.php; fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock; } location ~ /\.ht { deny all; } # Set expires header for console CSS and JS. # These files are timestamped with each new release, so it is safe to cache them agressively. location ~ "console_.*\.(js|css)$" { expires max; } # Catch image requests and pass them back to PHP if a cache does not yet exist location ~ "^/storage/cache/images(/(([0-9]{3}/[0-9]{3})|custom)/.*)$" { # Cached images have timestamps in the URL, so it is safe to set # aggresive cache headers here. expires max; try_files $uri /i.php?path=$1; } # Catch .css.lens requests and serve cache when possible location ~ "(lightbox-)?settings.css.lens$" { default_type text/css; try_files /storage/cache/site/${uri} /app/site/site.php?url=/$1settings.css.lens; } # Catch koken.js requests and serve cache when possible location ~ koken.js$ { default_type text/javascript; try_files /storage/cache/site/${uri} /app/site/site.php?url=/koken.js; } # Standard site requests are cached with .html extensions set $cache_ext 'html'; # PJAX requests contain the _pjax GET parameter and are cached with .phtml extensions if ($arg__pjax) { set $cache_ext 'phtml'; } # Do not check for a cache for non-GET requests if ($request_method != 'GET') { set $cache_ext 'nocache'; } # If share_to_tumblr cookie is preset, disable caching (long story) if ($http_cookie ~* "share_to_tumblr" ) { set $cache_ext 'nocache'; } # Catch root requests location ~ ^/?$ { try_files /storage/cache/site/index/cache.$cache_ext /app/site/site.php?url=/; } # All other requests get passed back to Koken unless file already exists location / { try_files $uri $uri/ /storage/cache/site/${uri} /storage/cache/site/${uri}cache.$cache_ext /app/site/site.php?url=$uri&$args; } # Original file directories location ~ "^/storage/originals(/(([0-9]{2}/[0-9]{2}))/.*)$" { deny all; } }