Heroku custom domains
I've setup two custom domains for my Heroku app.
example.com example.com.herokudns.com
*.example.com wildcard.example.com.herokudns.com
Domain configuration
I configured my domain as follows:
I added a CNAME Record for * pointing to wildcard.example.com.herokudns.com.
Works fine.
I forwarded my URL using GoDaddy's Domain Forwarding tool, because I can only specify IP addresses as A records.
Problem
The domain forwarding points to example.com.herokudns.com. Unfortunately GoDaddy automatically prepends http://, so it actually does not open my app and instead shows a Heroku message:
There's nothing here, yet.
Goal
Setting up my GoDaddy root domain to point to my Heroku app.
Note: GoDaddy automatically added an A record for # pointing to >>++FWD1++<<
Cloudflare does the job!
Finally, I achieved my goal of using my naked domain as host by choosing CloudFlare to handle my DNS configuration.
Resources:
CloudFlare allows CNAME Records to be the naked domain
How CNAME Flattening works
Note: CloudFlare has a pretty good documentation and setup process, you just need to:
add your domain to CloudFlare
follow the CloudFlare setup guide
updating your nameservers (in my case GoDaddy) to point to CloudFlare
What did you set your DNS to forward to? I had this same problem, but solved this creating a Heroku DNS entry for www.myapp.com. Heroku creates a DNS target of www.myapp.com.herokudns.com.
Here is my setup:
DNS forwarding to www.myapp.com
DNS CName of www to www.myapp.com.herokudns.com
Heroku DNS added www.myapp.com
You can also achieve this by setting up a nginx server with docker in five minutes.
First follow the instruction on this link: https://pentacent.medium.com/nginx-and-lets-encrypt-with-docker-in-less-than-5-minutes-b4b8a60d3a71
Then add your nginx server ip to your A Record with host name #
In you nginx server you can use this code to redirect all traffic to your naked domain to www domain. Just replace all your-domain.com with your domain.
server {
listen 80;
server_name your-domain.com;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://www.your-domain.com;
}
}
server {
listen 443 ssl;
server_name your-domain.com;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
location / {
return 301 https://www.your-domain.com$request_uri;
}
}
In Setup Heroku and GoDaddy? allegutta solves the issue by masking the heroku-app-name domain with the .com domain. Instead of slooob.com.herokudns.com, use your original heroku app url ([heroku-app].herokuapp.com) and it should work. Just worked for me.
Related
We have a Ruby/Rails website we're migrating from Heroku to AWS. The original dev is not available. I'm now trying to complete the migration. My background is in the Windows / .NET world. This Linux / Ruby/Rails environment is quite foreign to me...
Here's the current environment I've set-up:
Route 53
Record Name
Record Type
Alias
Alias Route Traffic To
foo.example.com
A
yes
cloudfront: xyz.cloudfront.net
CloudFront
Domain Name
Alternate Domain Names
Origin Domain
Origin Protocol
Behavior Protocol
xyz.cloudfront.net
foo.example.com
foo.us-west-2.elb.amazonaws.com
HTTP only
Redirect HTTP to HTTPS
The CloudFront distribution:
uses an AWS issued SSL cert
handles the http to https redirect
forwards the request to the ELB over http (not https)
Load Balancer
DNS Name
Listener Rule
Forward To
foo.us-west-2.elb.amazonaws.com
HTTP 80: default action
Target Group: foo-ec2
Target Group: foo-ec2 contains a single Ubuntu ec2 instance running nginx/1.18.0 + Phusion Passenger 6.0.10 to serve up the Ruby/Rails site.
nginx config
server {
listen 80 default_server;
listen [::]:80 default_server;
# SSL Config - we should NEVER receive 443/https traffic;
# CloudFront manages https traffic => AWS ELB => http to this server
#listen 443 ssl default_server;
#listen [::]:443 ssl default_server;
server_name foo.example.com
foo.us-west-2.elb.amazonaws.com;
# Tell Nginx and Passenger where the app's 'public' directory is
root /var/www/foo_example/public;
# Turn on Passenger
passenger_enabled on;
passenger_app_env production;
passenger_ruby /home/ubuntu/.rbenv/versions/2.6.8/bin/ruby;
}
Issue
The rails app starts up without error and is served over https. However, when a user attempts to log in / authenticate, the Devise gem sends back a redirect using http and the ELB's DNS name.
Example
sign_in request
Request URL: https://foo.example.com/users/sign_in
Request Method: POST
Status Code: 302
sign_in response
location: http://foo.us-west-2.elb.amazonaws.com/users
server: nginx/1.18.0 + Phusion Passenger(R) 6.0.10
status: 302 Found
Notice the request was over https and our domain:
https://foo.example.com
But now we're over http and the ELB's domain:
http://foo.us-west-2.elb.amazonaws.com
My assumption
The devise gem is seeing the host from the ELB and then generates the URL from the ELB host, creating two issues:
we are now on http since the ELB communicates with the ec2 instance over http
we are now on the ELB's host name, foo.us-west-2.elb.amazonaws.com, instead of our name, foo.example.com
I've looked into the devise documentation to see if we can just pass in the http protocol and domain to use when creating the post back, but my ruby knowledge is limited. Plus, I think this would be the "bad" path; where the "good" path would be to have the AWS ELB forward the actual domain name, instead of it's own.
I've reviewed several SO and related stack sites with similar questions, but I've either ended up with an infinite loop redirect, or the various config changes have resulted in the same behavior of the devise gem creating the wrong URL post back.
These two questions seem to be the closest, but I'm not quite able to make the "connection" between the answers and my limited knowledge of this ecosystem.
AWS Cloudfront + Load Balancer, url changes from main domain to load balancer subdomain
cloudfront domain replaced by application load balancer dns name when redirecting from http to https
Question
How can I get the AWS ELB to forward our domain, foo.example.com, to the ec2 target group and not the ELB's domain?
After more experimentation with AWS settings, the solution is actually rather simple. The other answers I posted in the question were vague in the actual settings, so here's the concrete solution.
In CloudFront, you need to create a new origin request policy, not a cache policy:
Open up CloudFront
go to Policies (left nav)
click the "Origin Request" tab
click the "create origin request policy" button
name the policy whatever you want, i.e., "my origin request policy"
under "Origin request settings" > Headers: select "Include the following headers"
under "Add header": check the "Host" option
click the "Create" button
The policy will look like this:
Once the new origin request policy has been created:
head back to the CloudFront distributions
click your distribution's Id so you can edit it
click the "Behaviors" tab
select your behavior and edit
scroll down to "Cache key and origin requests"
make sure the "Cache policy and origin request policy (recommended)" is selected
under the "Origin request policy - optional", select your new policy, i.e., "my origin request policy"
save changes
The behavior will look like this (I'm using no caching for now to verify the ec2 instance is getting all the requests):
That's it. The host header is now correctly passed through to the ELB and ec2 instance. Nothing else needs to be done with the ELB.
I verified the host header was being used in all requests by modifying the nginx logging option to include the $host variable in the log file (and did a bit more customization to the OOB format):
# prefixed log with '[my-log]', but it's not needed; remove.
log_format my-log '[my-log] $http_x_forwarded_for - $remote_user [$time_local] '
'"$request_method $scheme://$host$request_uri $server_protocol" '
'$status $body_bytes_sent "$http_referer" "$http_user_agent" $request_time';
server {
listen 80 default_server;
listen [::]:80 default_server;
# SSL Config - we should NEVER receive 443/https traffic;
# CloudFront manages https traffic => AWS ELB => http to this server
#listen 443 ssl default_server;
#listen [::]:443 ssl default_server;
server_name foo.example.com
foo.us-west-2.elb.amazonaws.com;
# create the our log file
access_log /var/log/nginx/my-log.access.log my-log;
# Tell Nginx and Passenger where the app's 'public' directory is
root /var/www/foo_example/public;
# Turn on Passenger
passenger_enabled on;
passenger_app_env production;
passenger_ruby /home/ubuntu/.rbenv/versions/2.6.8/bin/ruby;
}
Surely this will help future me as well as others.
For infinite redirects, I made this additional change and the site is serving fine.
Select Origin. Edit origin
Select Match viewer in Protocol. Save. Invalidate. Done
I have a Rails API and a web app(using express), completely separate and independent from each other. What I want to know is, do I have to deploy them separately? If I do, how can I make it so that my api is in mysite.com/api and the web app in mysite.com/
I've seen many projects that do it that way, even have the api and the app in separate repos.
Usually you don't expose such web applications directly to clients. Instead you use a proxy server, that forwards all incoming requests to the node or rails server.
nginx is a popular choice for that. The beginners guide even contains a very similar example to what you're trying to do.
You could achieve what you want with a config similar to this:
server {
location /api/ {
proxy_pass http://localhost:8000;
}
location / {
proxy_pass http://localhost:3000;
}
}
This is assuming your API runs locally on port 8000 and your express app on port 3000. Also this is not a full configuration file - this needs to be loaded in or added to the http block. Start with the default config of your distro.
When there are multiple location entries nginx chooses the most specific one. You could even add further entries, e.g. to serve static content.
While Svens answer is completely correct for the question given. I'd prefer doing it at the DNS level so that I can change the server to a new location just in case my API or Web App experience heavy load. This helps us to run our APIs without affecting WebApp and vice-versa
DNS Structure
api.mysite.com => 9.9.9.9 // public IP address of my server
www.mysite.com = > 9.9.9.9 // public IP address of my server
Since now you'd want both your WebApp and API to run on the same server, you can use nginx to forward requests appropriately.
server {
listen 80;
server_name api.mysite.com;
# ..
# Removed for simplicity
# ..
location / {
proxy_pass http://localhost:3000;
}
}
server {
listen 80;
server_name www.mysite.com;
# ..
# Removed for simplicity
# ..
location / {
proxy_pass http://localhost:8000;
}
}
Any time in future if you are experiencing overwhelming traffic, you can just alter the DNS to point to a new server and you'd be good.
I have given two different different websites names with same IP(192.168.1.142)
Now i am using both these for configuring the reverse proxy using nginx.
Is it ok whether i can run ?
Kindly suggest any problems if any that i might face in future.
Yes this is fine. Use different server {} blocks and the server_name option to specify which configuration is which:
server {
listen 80;
server_name domain.com;
# rest of domain.com options go here
}
server {
# this will be the default site on this host
listen 80 default_server;
server_name other.com;
# rest of other.com options go here
}
In practice, splitting the two server {} blocks into different files would make maintenance easier and is often the norm.
If you only want the sites to be available on that one IP, change the listen directive to:
listen 192.168.1.142:80;
Also if you want to use SSL/HTTPS then you may run into complications as you can only have one SSL certificate per IP address. There are solutions if this is the case.
I have an rails application running in Amazon EC2 and with files served in S3.
My problem is: All my application in running normally in http and I'd like to put on https. But, it's a pre-requisite that the same file responds either to http and https.
For example: if I have a file http://domain.s3.amazon.com/file.js, it should be respond to https://domain.s3.amazon.com/file.js as well.
My scripts will be used by other customers in http and https environments, so it's mandatory that its served as http and https, otherwise the browser will give this message:
[blocked] The page at 'https://mycustomerurl' was loaded over HTTPS, but ran insecure content from 'http://mydomain.com/myfile.js': this content should also be loaded over HTTPS.
How can I do that?
Thanks
PS: I've seen some samples, but the whole app goes to https, and I have this specific requisite
As long as the domain is the same, the easiest way to do this is to drop the protocol at the beginning of the url.
Just do a request for //domain.s3.amazon.com/file.js
I finally found a solution.
At the end, it was not an issue to be solved at application level but at server configuration level.
I've bought a certificate and installed in my server. Then I configured nginx with:
worker_processes 1;
http {
server {
listen 443;
ssl on;
ssl_certificate /usr/local/nginx/conf/cert.pem;
ssl_certificate_key /usr/local/nginx/conf/cert.key;
keepalive_timeout 70;
}
}
I've just deployed a Rails application using Capistrano under /home/username/app_name.
Now, I'm not quite sure on how to setup Nginx, I've followed this tutorial, http://coding.smashingmagazine.com/2011/06/28/setup-a-ubuntu-vps-for-hosting-ruby-on-rails-applications-2/comment-page-1/#comment-594321. I have this on my nginx.conf file, I modified the first server block I saw:
server {
listen 80;
server_name www.yourdomain.com;
root /home/johndoe/test_app/current/public;
passenger_enabled on;
...
}
But now, I'm not sure how to procede. What exactly is the server_name in the example above? And how should I access the application? I've tried typing in my ip address and nothing shows up. I'm using Linode by the way. And how do I set up the nameservers for my domain? Detailed explanations and tutorials would be very helpful. Thanks in advance!
server_name on Nginx is exactly the same as ServerName in Apache, i.e. the domain name you'd like to use for this directory (named virtual host).
With passenger set to on you should be able to access the application directly via the domain.