https 302 redirect issue when posting form

Hi all,

I am migrating my Yii application from one Apache hosted server to a Nginx server and for the most part it is working fine. I have created a self-signed certificate so I can view the site over ssl. I can move throughout the site fine over ssl but when I post a form the site is redirected back to http. I have viewed the headers and found that there is a 302 Moved Temporarily issue. The data

sent through the form submission is being saved as expected.

I am quite new to Nginx but after looking through the config many times I don’t think it’s the cause. I am also hosting a Wordpress blog under the same conditions and that is not suffering the same issue.

Does anyone have any thoughts on what might be happening?

OS: Debian 6.0 Squeeze

Web server: Nginx 1.0.6

Yii version: 1.1.2

You can use a filter to force https. See this blog for a demo.

Matt

Are you sure that you don’t force redirection after processing the form in Yii? If not, please post headers of the response you get.

Thanks for the replies.

I should have mentioned that ssl worked fine on the previous host under Apache. I just tested the application on my laptop with the same set-up (Linux, Nginx etc…) and found that the ssl issue is here also. I set-up a vhost record under Apache and the application is now working as expected which points the issue directly at Nginx.

I’m new to Nginx so I must have done something wrong in the config. Will post any findings here.

Sorry Van Damm, I forgot to include the headers. They are below, pasted from Chrome’s Firebug type tool. You can see that the form is posted over ssl but then the site is redirected to http.

HeadersCookiesTiming

Request URL:https://mysite/index.php?r=client/update&id=1

Request Method:POST

Status Code:302 Moved Temporarily

Request Headers

Accept:text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8

Accept-Charset:ISO-8859-1,utf-8;q=0.7,*;q=0.3

Accept-Encoding:gzip,deflate,sdch

Accept-Language:en-US,en;q=0.8

Cache-Control:max-age=0

Connection:keep-alive

Content-Length:690

Content-Type:application/x-www-form-urlencoded

Cookie:PHPSESSID=e6g06p2eftn00spgctnsbug5m1

Host:footprints

Origin:https://mysite

Referer:https://mysite/index.php?r=client/update&id=1

User-Agent:Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.30 (KHTML, like Gecko) Ubuntu/11.04 Chromium/12.0.742.112 Chrome/12.0.742.112 Safari/534.30

Query String Parametersview URL encoded

r:client/update

id:1

Form Dataview URL encoded

Client%5Bname%5D:Cust1

ContactInfo%5Baddress1%5D:7 Electric St.

ContactInfo%5Baddress2%5D:

ContactInfo%5Bcity%5D:somewhere

ContactInfo%5Bstate%5D:Vic

Client%5Bactive%5D:0

Client%5Bactive%5D:1

yt0:Save

Response Headers

Cache-Control:no-store, no-cache, must-revalidate, post-check=0, pre-check=0

Connection:keep-alive

Content-Type:text/html

Date:Fri, 30 Sep 2011 09:24:38 GMT

Expires:Thu, 19 Nov 1981 08:52:00 GMT

Location:http://mysite/index.php?r=client/view&id=1

Pragma:no-cache

Server:nginx/0.8.54

Transfer-Encoding:chunked

X-Powered-By:PHP/5.3.5-1ubuntu7.2

So it’s definitely a server issue. Can you please post your nginx config for the site? (global config too)

The main config file should be unchanged from the installed one.

/etc/nginx/nginx.conf


user www-data;

worker_processes 4;

pid /var/run/nginx.pid;


events {

	worker_connections 768;

	# multi_accept on;

}


http {


	##

	# Basic Settings

	##


	sendfile on;

	tcp_nopush on;

	tcp_nodelay on;

	keepalive_timeout 65;

	types_hash_max_size 2048;

	# server_tokens off;


	# server_names_hash_bucket_size 64;

	# server_name_in_redirect off;


	include /etc/nginx/mime.types;

	default_type application/octet-stream;


	##

	# Logging Settings

	##


	access_log /var/log/nginx/access.log;

	error_log /var/log/nginx/error.log;


	##

	# Gzip Settings

	##


	gzip on;

	gzip_disable "msie6";


	# gzip_vary on;

	# gzip_proxied any;

	# gzip_comp_level 6;

	# gzip_buffers 16 8k;

	# gzip_http_version 1.1;

	# gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;


	##

	# Virtual Host Configs

	##


	include /etc/nginx/conf.d/*.conf;

	include /etc/nginx/sites-enabled/*;

}




#mail {

#	# See sample authentication script at:

#	# http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript

# 

#	# auth_http localhost/auth.php;

#	# pop3_capabilities "TOP" "USER";

#	# imap_capabilities "IMAP4rev1" "UIDPLUS";

# 

#	server {

#		listen     localhost:110;

#		protocol   pop3;

#		proxy      on;

#	}

# 

#	server {

#		listen     localhost:143;

#		protocol   imap;

#		proxy      on;

#	}

#}



Vhost file:


# You may add here your

# server {

#	...

# }

# statements for each of your virtual hosts to this file


##

# You should look at the following URL's in order to grasp a solid understanding

# of Nginx configuration files in order to fully unleash the power of Nginx.

# http://wiki.nginx.org/Pitfalls

# http://wiki.nginx.org/QuickStart

# http://wiki.nginx.org/Configuration

#

# Generally, you will want to move this file somewhere, and start with a clean

# file but keep this around for reference. Or just disable in sites-enabled.

#

# Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples.

##


server {

	listen   80; ## listen for ipv4; this line is default and implied

	#listen   [::]:80 default ipv6only=on; ## listen for ipv6


	#root /usr/share/nginx/www;

	root /var/www/somedomain.com/public_html;

	index index.php index.html index.htm;


	# Make site accessible from http://localhost/

	server_name somedomain.com;


        #log_format  main  '$remote_addr $host $remote_user [$time_local] "$request"

        #        $status $body_bytes_sent "$http_referer" "$http_user_agent" $ssl_cipher $request_time';

        #access_log  /var/log/nginx/somedomain.com/access.log main buffer=32k;

        #error_log   /var/log/nginx/somedomain.com/error.log info;


	auth_basic            "Restricted";

	auth_basic_user_file  /etc/nginx/htpasses/somedomain;


	location / {

		# First attempt to serve request as file, then

		# as directory, then fall back to index.html

		# try_files $uri $uri/ /index.html;

		try_files $uri $uri/ /index.php;

	}


	location /doc {

		root /usr/share;

		autoindex on;

		allow 127.0.0.1;

		deny all;

	}


	location /images {

		root /usr/share;

		autoindex off;

	}


	#error_page 404 /404.html;


	# redirect server error pages to the static page /50x.html

	#

	#error_page 500 502 503 504 /50x.html;

	#location = /50x.html {

	#	root /usr/share/nginx/www;

	#}


	# proxy the PHP scripts to Apache listening on 127.0.0.1:80

	#

	#location ~ \.php$ {

	#	proxy_pass http://127.0.0.1;

	#}


	# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000

	#

	location ~ \.php$ {

    		root /var/www/somedomain.com/public_html;

    		if (!-f $request_filename) {

        		return 404;

    		}


    		fastcgi_pass   127.0.0.1:9000;

    		fastcgi_index  index.php;

    		fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;

    		include        fastcgi_params;

	}


	# deny access to .htaccess files, if Apache's document root

	# concurs with nginx's one

	#

	#location ~ /\.ht {

	#	deny all;

	#}

}




# another virtual host using mix of IP-, name-, and port-based configuration

#

#server {

#	listen 8000;

#	listen somename:8080;

#	server_name somename alias another.alias;

#	root html;

#	index index.html index.htm;

#

#	location / {

#		try_files $uri $uri/ /index.html;

#	}

#}




# HTTPS server

#

server {

	listen 443;

	server_name somedomain.com;


	root /var/www/somedomain.com/public_html;

	index index.php;


	auth_basic            "Restricted";

	auth_basic_user_file  /etc/nginx/htpasses/somedomain;


	ssl on;

	ssl_certificate /etc/nginx/certs/somedomain.com_server.crt;

	ssl_certificate_key /etc/nginx/certs/somedomain.com_server.key;


	ssl_session_timeout 5m;


	ssl_protocols SSLv3 TLSv1;

	ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv3:+EXP;

	ssl_prefer_server_ciphers on;


	location ~ \.php$ {

    		root /var/www/somedomain.com/public_html;

    		if (!-f $request_filename) {

        		return 404;

    		}


    		fastcgi_pass   127.0.0.1:9000;

    		fastcgi_index  index.php;

    		fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;

    		include        fastcgi_params;

	}


	location / {

		try_files $uri $uri/ /index.php;

	}

}



I have an interesting development that might help. I am now trying to migrate my wiki (mediawiki) from the same Apache server to the new Nginx server and have found a somewhat opposite problem. I created the virtual host record from the previous one just shown above but changed the hostname and paths to match.

When I load the site in the browser at http://wiki.mydomain.com is get a 302 permanent redirect to http://wiki.mydomain.com:443/index.php?title=Main_Page . The server in this case then returns the error:


400 Bad Request


The plain HTTP request was sent to HTTPS port

Using Google I found I should try adding “fastcgi_param HTTPS on;” to the PHP section of the config (http or https isn’t specified). However, this has not changed anything.

I guess this issue is not really worthy of being in the Yii forum anymore but still open to suggestions of course.

:) there is nothing to be done with a server cnfiguration, Van Damm is wrong

you have

Location:http://mysite/index.php?r=client/view&id=1

in your response (not https)

this header is defined in your scripts.

Fix the redirect - make relative redirects, like /index.php?r=client/view&id=1 - don’t define the protocol in the URI

Please tell us the return value of


Yii::app()->getRequest()->getIsSecureConnection();

So, if you view the "view" URL manually, and you change http to https, does the server allow you to view that URL via HTTPS?

When you start up Nginx, do you see any errors directly output to the screen? How about to the logs?

If there’s a problem with SSL on the server, you should see an error somewhere from Nginx.

Are you certain you’re not hard-coding the HTTP redirect?

Additionally, until we debug this issue, please ensure all other vhosts are disabled.

The logs don’t seem to indicate any issues and my redirect command is:


$this->redirect(array('view','id'=>$model->id));

I can navigate throughout the site fine in https. It’s only when a form (any form) is submitted that the site switches to http.

Mentel has guided me to the right area of the issue. Running


die( var_dump( Yii::app()->getRequest()->getIsSecureConnection() ) );

just before the redirect returns false. I guess this shows that Yii thinks the site is running in http mode. However, at this time the browser url still indicates https.

A quick search led me to this Yii forum post which shows that Yii relies on the server sending a $_SERVER[‘HTTPS’] header when running under https. A var_dump() of $_SERVER under both Apache and Nginx showed me that Nginx does NOT send this header and therefore this is the cause of my problem.

I will investigate further and advise of any results.

I should also mention that I have added the line "fastcgi_param HTTPS on;" to my vhost record and confirmed that it is being read by dumping $_SERVER again but it seems in this instance that the redirect is ignoring this.

You’re welcome.

Workarounds:

  1. You can add the header yourself. See nginx docs.

  2. You can edit CHttpRequest.php.

Fix:

You should open a ticket for Yii to fix this.

It shouldn’t be nginx’s fault since there’s no specification for this header in RFC 2818.

It seems that by adding the ‘HTTP on’ parameter did correct the issue. Possibly I didn’t notice it because I was updating a local vhost file while testing remotely or the other way around. However I believe this workaround is not ideal and I would like to see this corrected in Yii.

I corrected the issue of not needing to use a non standard http header by changing the Yii core method CHttpRequest::getIsSecureConnection() in web/CHttpRequest.php to the following:


    public function getIsSecureConnection()

    {

        return isset($_SERVER['SERVER_PORT']) && !strcasecmp($_SERVER['SERVER_PORT'],'443');

    }



I have also taken mentel’s suggestion and submitted a bug with my suggestion which can be found here.

Thanks all for your suggestions on solving this.

Glad to help.

Although my suggested fix was added to the Yi code base, it was reverted back because a site running on port 443 does not necessarily mean it over SSL. There is no standard header that browsers send when running over SSL so therefore a non-ideal solution must be used. I believe the best solution is to add the HTTPS header to the Nginx config as suggested.

Yes, I agree.

The suggested workaround should do.