There are many tags in the HTML which NextJS generates which contain absolute URLs. For example:
href="/_next/blah"
I've added selenium + chrome headless testing to my Next project, and it works fine when I test http://localhost:3000, but when I do a next export
, and then try to to test file:///home/user/project_dir/out/index.html
, all of those absolute links are broken.I.e, it tries to fetch file:///_next/blah
instead of: file:///home/user/project_dir/out/_next/blah
.
If I post-edit the generated HTML to make those links relative, then it all works fine.
I need to be able to run my tests against the static files which are generated by export to ensure that they have been generated correctly before publishing.
I suspect this is also an issue when people simply wish to run their nextjs application from a location other than the root of their website.
| Tech | Version |
|---------|---------|
| next | v3.0.1-beta.15 |
| node | v8.1.3 |
| OS | Debian 9 |
| browser | Google Chrome 59.0.3071.115 |
@mikecardwell Other issues are talking about this. The solution is to use assetPrefix.
That is not a very good solution as it would require me to build one set of files to be served by a webserver, and then a different set of files to be accessed via a file:/// url. A better solution would be the one I described where relative URLs are used throughout.
@mikecardwell I meant that you can use assetPrefix
to achieve your described solution (create relative paths):
assetPrefix: './'
But I still can't see why is it required to preview locally via file URI? Couldn't you use serve or any other http server locally?
Making the urls relative won't fix the issue. Since then you'd have issues when there's nested pages, pages/some-dir/some-route.js
(some-dir/some-route
route) would get a could not find some-dir/some-route/_next/*
. The right solution is to use for example serve to serve the html files through a webserver. That would be a better test anyway right, since users will get the html served through a webserver 🤔
@timneutkens - Your example doesn't work because @Blistok's solution was wrong and would not work. When I say to use relative URLs, I mean that each html page which is generated would know where it is relative to the content it is trying to link to. For example when generating "some-dir/some-route", it would know that it is 2 levels deep, and it would know that the _next directory is one level deep, so when generating that particular page of HTML, it would use "../_next/blah". When generating another page for the same site, it might need to use "../../../_next/blah". This isn't just the "_next" directory I'm talking about either, it's all resources, e.g. link tags etc.
As it stands, "next export" doesn't produce a website which can be served from anywhere, it spits out a website that can be served from a particular subset of URL structures.
Here is a real life example of a website where that is a problem: http://www.exim.org - That is a statically generated website. It also has a large number of mirrors - http://www.exim.org/mirmon/www_mirrors.html - Many of these mirror a website from a sub url. E.g http://mirror.easyname.at/exim/ - The mirrors are updated by simply rsyncing the static files from the source on a schedule. This setup could not work with the files that nextjs spits out, because they are not portable.
Here's another use case: I want to build a nextjs app which I can just export to disk, zip up and distribute it as an application for people to unzip on their desktops/laptops and open in their web browser using "File -> Open". I can't do that with next export as it stands, because everyone will be opening the project from a different directory.
HTML documentation that comes packaged along with the application in a .tgz... Step 1 for reading this documentation, install a web server?
If nextjs used relative urls then you wouldn't need to tell people who want to serve up their site on things like github pages to add assetPrefix to their nextjs configuration. It would "just work".
[edit] You closed this issue prematurely IMO. I hope that doesn't mean my response is ignored.
Support for exporting a static site with internal links and asset references all being relative urls would be rad for statically hosting next.js apps on ipfs or, as @mikecardwell explains, any situation where the site needs to be self-contained and can't assume a specific mounting path.
+1 Will be like, "woah, man" once Mike's ideas become reality.
@timneutkens I am re-opening this. It's causing Next.js apps that are routed through a Path Alias rule to return the incorrect hostname which leads to potential CSP issues, etc.
It would be nice to have a good solution for this.
@TooTallNate we use path alias for zeit/docs
without any issues using absolute URLs. I really don't like to use relative URLs since they complicate things unnecessary.
The state is undefined when someone use a relative URL inside a component which used in multiple pages.
@arunoda The router could be extended with a function to allow you to look up the current path to the root of the app. Then people could just wrap such components in the withRouter HOC and then do things like:
<img src={ `${this.props.router.pathToRoot}/static/images/foo.png` }/>
Which would sometimes spit out <img src="./static/images/foo.png"/>
and other times <img src="../../../static/images/foo.png"/>
depending on the current URL.
Of course, people who are happy with absolute URLs can just continue doing what they're doing right now, but for all of the use cases which require relative URLs, a solution would exist.
I'd be totally happy with a manual solution to this like @mikecardwell suggests.
Gonna +1 this issue.
Perhaps this is a specific use case (but I have seen others complaining).
My situation is that I am running multiple nextJS apps in a Kubernetes cluster with a NGINX ingress module getting requests from an AWS ELB. Each app is assigned a namespace such as mysite.com/app1
or mysite.com/app2
. The NGINX ingress is configured such that when a request is received on /app1
, it forwards it to the root (/
) of the container running app1 (because app1
is expecting requests on /
).
To make links work (see: href
), the NGINX instance adds a base
element to the head
of the page, so that each URL (such as going to /contact-us
on a example page) is prepended with our base, hence we get /contact-us
=> mysite.com/app1/contact-us
and everyone is happy!
This of course, doesn't work with nextJS as the paths are all absolute instead of relative (base
only works on relative paths) and since this base
is being added by NGINX on requests, it is difficult to guess the URL during build (auto deploy URLs/Machine Generated URLS).
Would love to see support for either base
, relative paths, or perhaps a completely different solution!
Any solutions to this? I am running nextjs static export from cordova which doesn't have webserver and serves pages as file://
so need to have relative paths
I think the only current solution is to post process the exported HTML to modify the links.
Thanks, the main issue with pathanmes like '/'
or '/home'
imagine doing search and replace of string '/'
not a pretty task
Yeah, it's rubbish. I'd be tempted to leverage headless chrome via the Puppeteer library to modify the links.
I guess the alternative would be to modify NextJS to give it the support I described earlier in this thread. I don't think the core devs are interested in making those changes themselves.
Same issue here, I have used assetPrefix: './'
which fixed the href
of the link tags. However my static assets, like images and videos links, are broken.
An option to export as relative paths (just don't prefix everything with /
) would be very desirable. I expect to just be able to open index.html of my exported files and see things load. What's the reason to make it absolute by default?
for cordova i solved this using cordova local webserver
@Yuripetusko
Great! I would be happy if you could share sample project or code .
At present, there is little information on next.js with cordova,
So it will be very very helpfull to anyone who want to use it.
@tky5622 ok good call, will write something more formal in next few days
@Yuripetusko Can you explain fast how you did? :)
+1 on this issue
My use case is a bit different. I'd like to use Next to build an app and distribute that over IPFS. When an app is running through IPFS, it's impossible to know the path it will be running in, so using assetPrefix
isn't an option.
For example, my app could be running in any of the following:
http://<gateway-address>/ipfs/<cid>/index.html
(where <cid>
is the hash of the content)http://<gateway-address>/ipns/<some-string>/index.html
http://<some-domain>/index.html
when there's a domain serving the request (e.g. when using the Cloudflare IPFS gateway)Can't this be solved with the base
tag?
No, because the base path is not always known or fixed. See my comment above.
Also my comment from a couple months ago https://github.com/zeit/next.js/issues/2581#issuecomment-439286024 references the base tag.
I have the same problem right now, i'm running a instance of Puppetter that would access nextjs exported files and print pdf's of them, but the assets are not getting served.
Edit: Finally I've got it working adding base url to the html head, before rendering it on puppetter, i had to add a flag on it to disable security (internal chromium flag), and all works as expected
I too am having this issue. I'm contracting for the government in Australia creating educational content using next, part of the requirement (antiquated though it may be) when we want to publish this educational website is that it should be a 'self-contained zip'.
Is there no export option that can be added to make this 'just work'? Find and replace or custom post processing is cumbersome.
Also, it's strange that the documentation doesn't mention that a HTTP server is required. For next users like myself, it'd be great if you were more explicit about the fact that the output of next export is not self-contained and requires a server
I too am having this issue. My case is that I need to place static files generated by next.js to server in Hybrid APP where html files are visited through file:// protocol and the absolute path is not a fixed value.
If you use ionic webview plugin then you can make it work since ionic runs local webserver serving your files to http://localhost:8081 then you just copy nextjs static export files to www folder and only export index.html and let other routes resolve using client side (SSR not needed)
Thank you @Yuripetusko, I can confirm it works for me. I just created a new Cordova project, added the plugin, copied the static files in www/
and links work just fine. Something like this:
cordova create hello com.example.hello HelloWorld
cd hello/
cordova platform add ios
cordova plugin add https://github.com/ionic-team/cordova-plugin-ionic-webview
cd path/to/nextjs
npm run build && npm run export
cp -R out/* path/to/hello/www/
cd path/to/hello
cordova emulate
I also had to add <preference name="ScrollEnabled" value="true" />
to config.xml
because of https://github.com/ionic-team/cordova-plugin-ionic-webview#scrollenabled:
Ionic apps work better if the WKWebView is not scrollable, so the scroll is disabled by default, but can be enabled with this preference. This only affects the main ScrollView of the WKWebView, so only affects the body, not other scrollable components.
I'm still testing, so it's possible there are other issues.
To host on IPFS it is possible to use a hack where you dynamically inject <base href="...">
tag at runtime:
// ./pages/_document.js
import Document, { Html, Head, Main, NextScript } from 'next/document'
const scriptTxt = `
(function () {
const { pathname } = window.location
const ipfsMatch = /.*\\/Qm\\w{44}\\//.exec(pathname)
const base = document.createElement('base')
base.href = ipfsMatch ? ipfsMatch[0] : '/'
document.head.append(base)
})();
`
class MyDocument extends Document {
render() {
return (
<Html>
<Head>
<script dangerouslySetInnerHTML={{__html: scriptTxt}}/>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
export default MyDocument
There are also other issues that arise with it such as relative assetPrefix and routing, which all need their own workarounds.
You can see a small example of a Next.js app deployable to IPFS (with my current workarounds) here: https://github.com/Velenir/nextjs-ipfs-example/
(slightly contrary opinion) I found the current behaviour more favourable for my flow where I keep on moving my components around while organising and using an absolute link means I can easily move the components that use Link
without any issues.
I don't know if this works for any case (I just have an index page), but this is what I ended up doing:
assetPrefix
to assets
in config filenext build
next export
mv out/_next out/assets/_next
Now I can serve the files anywhere I want to
🤷♀️
still cant find any solutions after next js 9 in 2020 :((
1) Add next.config.js file with module.exports = { assetPrefix: './' } (you can have more properties in the module according to your code requirements )
2) Make change in package.json file in scripts with "build": "next build && next export"
3) Make changes to all next/link - Link herf value's (for eg. index page - earlier (href="/") after change (href="./index.html") and for all other pages like this earlier (href="/PAGE_NAME") after change (href="./PAGE_NAME.html")
1) PAGE_NAME represent placeholder for your page name.
2) To access all your public folder content of next js for eg for image use src="./image.jpg" instead of "/image.jpg".
3) The "build": "next build && next export" in above Main point 2 will create a out folder with your build files that works ("build": "next build" that creates build files in .next folder won't work neither using next.config.js file with module.exports = { distDir: 'BUILD_FOLDER_NAME' } will not work)
(BUILD_FOLDER_NAME is a placeholder)
I guess as of right now the issue is that the javascript code have no way of figuring out the base without extra configuration, after all the url are written as /xxx/yyy/zz instead of xxx#yyy/zzz, and because of that, it is difficult to figure out where the actual base path is just from the url itself.
In my case, this config helped:
module.exports = withPlugins([[withSass], [withImages]], {
webpack(config, options) {
config.resolve.modules.push(path.resolve("./"));
return config;
},
basePath: "/xxx",
assetPrefix: "/xxx"
});
Most helpful comment
@timneutkens - Your example doesn't work because @Blistok's solution was wrong and would not work. When I say to use relative URLs, I mean that each html page which is generated would know where it is relative to the content it is trying to link to. For example when generating "some-dir/some-route", it would know that it is 2 levels deep, and it would know that the _next directory is one level deep, so when generating that particular page of HTML, it would use "../_next/blah". When generating another page for the same site, it might need to use "../../../_next/blah". This isn't just the "_next" directory I'm talking about either, it's all resources, e.g. link tags etc.
As it stands, "next export" doesn't produce a website which can be served from anywhere, it spits out a website that can be served from a particular subset of URL structures.
Here is a real life example of a website where that is a problem: http://www.exim.org - That is a statically generated website. It also has a large number of mirrors - http://www.exim.org/mirmon/www_mirrors.html - Many of these mirror a website from a sub url. E.g http://mirror.easyname.at/exim/ - The mirrors are updated by simply rsyncing the static files from the source on a schedule. This setup could not work with the files that nextjs spits out, because they are not portable.
Here's another use case: I want to build a nextjs app which I can just export to disk, zip up and distribute it as an application for people to unzip on their desktops/laptops and open in their web browser using "File -> Open". I can't do that with next export as it stands, because everyone will be opening the project from a different directory.
HTML documentation that comes packaged along with the application in a .tgz... Step 1 for reading this documentation, install a web server?
If nextjs used relative urls then you wouldn't need to tell people who want to serve up their site on things like github pages to add assetPrefix to their nextjs configuration. It would "just work".
[edit] You closed this issue prematurely IMO. I hope that doesn't mean my response is ignored.