We have an Android mobile app built with Capacitor. The app was working fine in Capacitor 7 and has the following configuration in capacitor.config.ts:
import type { CapacitorConfig } from "@capacitor/cli"
const config: CapacitorConfig = {
appId: "xxx.yyy.zzz",
appName: "My App",
webDir: "dist",
server: {
hostname: "host.mydomain.com"
}
}
export default config
Notably, we set the server.hostname configuration option so that our API server can securely manage the CORS header “Access-Control-Allow-Origin” in its responses. In particular, if we left the server.hostname as the default value of “localhost”, then we have to configure our API server to allow cross-origin requests originating from “https://localhost”, which is obviously not secure, because anyone can control the domain name of “locahost”.
Long story short, we upgraded from Capacitor 7 to Capacitor 8, and now the server.hostname configuration option seems to be non-functional. Even though we set our usual custom hostname for hostname.server, the app still uses “locahost” as a hostname. This results in every API request to our server being rejected for violating CORS policy.
Is there something new in Capacitor 8 regarding the usage of hostname.server configuration option? Has anyone else experienced this problem after upgrading?
Thank you
I don’t see a problem with localhost
CORS only applies in a web browser so outside of a browser, anyone can call your API endpoint (from backend code or an API client). The Capacitor’s web view is also self contained within the native app, so I don’t see a risk there? Also, what stops someone else using your hostname in a different native app that calls your API or even just locally with your hostname but their DNS for it pointed to localhost? As far as I know, there is no hostname verification in the native web wrapper.
The Config docs say:
It is recommended to keep this as localhost as it allows the use of Web APIs that would otherwise require a secure context such as navigator.geolocation and MediaDevices.getUserMedia.
CORS isn’t intended to stop someone from calling our APIs– it’s meant to prevent attackers from duping unsuspecting users into calling our APIs from an origin that an attacker controls. I mean, you are correct that there is no security threat from the viewpoint of the APIs being called from capacitor within a captive WebView. However, our server has hundreds of other APIs (for our other traditional web apps) where we don’t want to list localhost as a valid origin. Doing so would allow an attacker to dupe unsuspecting users into calling those APIs from a web application that looks like our application, but is really running locally on the victim’s local computer. This the case that CORS policies are intended to prevent.
I get what you are saying but you say
To do that, the attacker would have to have compromised the user’s computer and be running a local web server under localhost. At that point, the user has bigger problems as the attacker can do anything (keylog, change DNS, steal browser sessions, etc.) and there is nothing we can do to keep the user safe 
EDIT
And sorry for side tracking your original question
Have you looked at open issues on the Capacitor repo to see if anyone else if having a similar issue?
I dunno, it feels like if allowing an origin of localhost were so secure, then browsers wouldn’t need to enforce CORS violations from the localhost origin. But no browser does that– they make the server return a Access-Control-Allow-Origin header indicating that it’s allowed. It’s just not something we want to risk.
It turns out that I had a missing curly-brace (syntax error) in the capacitor.config.ts file, which was causing the server.hostname setting to not get picked up… duh.
Sorry for the spurious post….
I do have another issue on the iOS platform related to usage of the SWP over Cocoapods, but I will open another post for that.
1 Like