The Future Studio team (and company) is based in Germany and due to that fact, we needed to comply the VAT rules of the European Union while launching the Future Studio University. Precisely, because we’re located inside the EU, we need to make sure everyone enrolling from a EU country needs to pay the VAT rate of that country.
Well, you already did the catch in your mind that the location is somewhat related to the user’s IP address. To determine your location, we use IP geo location and preselect your country within the enrollment form. We’ve published an open source hapi plugin called hapi-geo-locate that adds the geo-location functionality to your hapi project.
This guide shows you how to access the client IP address in two different ways: running your hapi server with and without a reverse proxy.
Before jumping right into the technical details, have a look at other tutorials within this extensive hapi series.
hapi Series Overview
- What You’ll Build
- Prepare Your Project: Stack & Structure
- Environment Variables and Storing Secrets
- Set Up MongoDB and Connect With Mongoose
- Sending Emails in Node.js
- Load the User’s Profile Picture From Gravatar Using Virtuals in Mongoose
- Implement a User Profile Editing Screen
- Generate a Username in Mongoose Middleware
- Displaying Seasons and Episodes for TV Shows with Mongoose Relationship Population
- Implementing Pagination for Movies
- Implement a Watchlist
- Create a Full Text Search with MongoDB
- Create a REST API with JSON Endpoints
- Update Mongoose Models for JSON Responses
- API Pagination for TV Shows
- Customize API Endpoints with Query Parameters
- Always Throw and Handle API Validation Errors
- Advanced API Validation With Custom Errors
- Create an API Documentation with Swagger
- Customize Your Swagger API Documentation URL
- Describe Endpoint Details in Your Swagger API Documentation
- 10 Tips on API Testing With Postman
- JWT Authentication in Swagger API Documentation
- API Versioning with Request Headers
- API Login With Username and Password to Generate a JWT
- JWT Authentication and Private API Endpoints
- Refresh Tokens With JWT Authentication
- Create a JWT Utility
- JWT Refresh Token for Multiple Devices
- Check Refresh Token in Authentication Strategy
- Rate Limit Your Refresh Token API Endpoint
- How to Revoke a JWT
- Invalidate JWTs With Blacklists
- JWT Logout (Part 1/2)
- JWT “Immediate” Logout (Part 2/2)
- A Better Place to Invalidate Tokens
- How to Switch the JWT Signing Algorithm
- Roll Your Own Refresh Token Authentication Scheme
- JWT Claims 101
- Use JWT With Asymmetric Signatures (RS256 & Co.)
- Encrypt the JWT Payload (The Simple Way)
- Increase JWT Security Beyond the Signature
- Unsigned JSON Web Tokens (Unsecured JWS)
- JWK and JWKS Overview
- Provide a JWKS API Endpoint
- Create a JWK from a Shared Secret
- JWT Verification via JWKS API Endpoint
- What is JOSE in JWT
- Encrypt a JWT (the JWE Way)
- Authenticate Encrypted JWTs (JWE)
- Encrypted and Signed JWT (Nested JWT)
- Bringing Back JWT Decoding and Authentication
- Bringing Back JWT Claims in the JWT Payload
- Basic Authentication With Username and Password
- Authentication and Remember Me Using Cookies
- How to Set a Default Authentication Strategy
- Define Multiple Authentication Strategies for a Route
- Restrict User Access With Scopes
- Show „Insufficient Scope“ View for Routes With Restricted Access
- Access Restriction With Dynamic and Advanced Scopes
- hapi - How to Fix „unknown authentication strategy“
- Authenticate with GitHub And Remember the Login
- Authenticate with GitLab And Remember the User
- How to Combine Bell With Another Authentication Strategy
- Custom OAuth Bell Strategy to Connect With any Server
- Redirect to Previous Page After Login
- How to Implement a Complete Sign Up Flow With Email and Password
- How to Implement a Complete Login Flow
- Implement a Password-Reset Flow
- Views in hapi 9 (and above)
- How to Render and Reply Views
- How to Reply and Render Pug Views (Using Pug 2.0)
- How to Create a Dynamic Handlebars Layout Template
- Create and Use Handlebars Partial Views
- Create and Use Custom Handlebars Helpers
- Specify a Different Handlebars Layout for a Specific View
- How to Create Jade-Like Layout Blocks in Handlebars
- Use Vue.js Mustache Tags in Handlebars Templates
- How to Use Multiple Handlebars Layouts
- How to Access and Handle Request Payload
- Access Request Headers
- How to Manage Cookies and HTTP States Across Requests
- Detect and Get the Client IP Address
- How to Upload Files
- Quick Access to Logged In User in Route Handlers
- How to Fix “handler method did not return a value, a promise, or throw an error”
- How to Fix “X must return an error, a takeover response, or a continue signal”
- Query Parameter Validation With Joi
- Path Parameter Validation With Joi
- Request Payload Validation With Joi
- Validate Query and Path Parameters, Payload and Headers All at Once on Your Routes
- Validate Request Headers With Joi
- Reply Custom View for Failed Validations
- Handle Failed Validations and Show Errors Details at Inputs
- How to Fix AssertionError, Cannot validate HEAD or GET requests
Detect the Client IP Address
There’s a straight forward way to determine the user’s IP address from a given request. The request.info
object provides information about the incoming request and includes details like the received
time, host
and hostname
and also the remoteAddress
which includes the user’s IP as a value.
server.route({
method: 'GET',
path: '/',
handler: function (request, reply) {
const ip = request.info.remoteAddress
return reply('client IP: ' + ip)
}
})
That’s it, you’ve the client IP address available for further processing. Ok, to be honest: this isn’t the way that works in all setups. We need to have a look at proxied requests as well. But before diving into that, you can test the code snippet from above.
If you want to verify this functionality, run a sample hapi server with the route from above and request it right from your local machine. You’ll see the 127.0.0.1
result for the user’s IP address within request.info.remoteAddress
. If you access the same server from your phone within your private home network, you’ll see the phone’s IP address which is different from 127.0.0.1
.
There’s a downside of this approach when running your hapi server behind a reverse proxy: the proxy will be the client and therefore all your requests would have the IP address of your proxy. Read on to get the trick on how to get the user’s IP address in those situations.
Client IP Address Behind a Reverse Proxy
A setup where the hapi server runs behind a reverse proxy (like nginx) requires extra programming effort to extract the correct client IP address. Because the proxy is the termination to the Internet and forwards the request internally, you’ll receive the proxy’s IP address within request.info.remoteAddress
for all requests.
That’s why proxies offer the feature to store the actual request IP address within an HTTP header. Using nginx as an example, you’d either check within your hapi server for request headers like x-forwarded-for
or real_ip
. That depends on your nginx configuration and is beyond the scope of this tutorial. Here, we’re focused on the hapi configuration.
The proxied header (e.g. x-forwarded-for
) includes the client IP and all proxy IPs the request went through in a comma separated list. The user’s IP is at the first position:
'x-forwarded-for': '1.2.3.4, 5.6.7.8, 10.11.12.13'
In the example above, 1.2.3.4
is the user’s IP address and 5.6.7.8
and 10.11.12.13
are IP addresses from proxy servers your request went through on its way to your application.
To extract the user’s IP address from the forwarded reverse proxy header, you need to fetch the first address:
server.route({
method: 'GET',
path: '/',
handler: function (request, reply) {
const xFF = request.headers['x-forwarded-for']
const ip = xFF ? xFF.split(',')[0] : request.info.remoteAddress
return reply('client IP: ' + ip)
}
})
The code snippet above combines both ways to fetch the client IP: the proxy header and hapi’s remoteAddress
.
Please notice that a value received within the proxied header is preferred over the IP provided by hapi. And the reason for that: hapi will always provide an IP address but it’s not always the correct one.
You can simply adjust the header to your needs if you’re behind another reverse proxy or web server.
Outlook
In situations where you need to rely on the user’s IP address, it’s important to make sure you’re actually determining the correct one. Depending on your setup, please have the functionality in place to get the correct IP address even though your hapi server runs behind a reverse proxy.
We’re happy to hear your thoughts and comments. Please don’t hesitate to use the comments below or find us on Twitter @futurestud_io. Let us know what you think!
Enjoy coding & make it rock!
Additional Resources
- hapi-geo-locate plugin to geo locate requests by IP address and provide
request.location
within your route handlers