knitr::opts_chunk$set(collapse = TRUE, comment = "#>", eval = FALSE)
Your app works. Every page, every link, every plot, at
http://127.0.0.1:3000. You deploy it, open
https://example.com/myapp/, and the home page shows up unstyled — or the
links go nowhere, or a page comes back "Not found".
Nothing is broken. The app simply does not know it has been moved.
Look at the address your users will type.
https://example.com/ — the app is at the root of its domain. Nothing to do,
stop reading.https://example.com/myapp/ — the app is served under a prefix. Read on.In a normal Shiny app the url is decoration. In brochure it is the app: the url is what decides which page you get.
You wrote:
page(href = "/contact", ui = tagList(h1("Contact")))
Deployed under /myapp/, the browser asks for /myapp/contact. That is not
/contact, so no page matches, and you get the 404. The other half of the
problem is the reverse: your link <a href="/contact"> sends the visitor to
https://example.com/contact, outside the app entirely.
Tell the app where it lives:
brochureApp( page(href = "/", ui = tagList(h1("Home"))), page(href = "/contact", ui = tagList(h1("Contact"))), basepath = "myapp" )
That is all. Your page() hrefs stay as they are — / and /contact, written
as if the app were at the root — and brochure adds and removes the prefix on
the way in and out.
"myapp", "/myapp" and "/myapp/" all mean the same thing, so you do not
have to remember which one it wanted.
Nothing to do. Connect tells the app where it is mounted, on every request, and brochure picks it up:
brochureApp( page(href = "/", ui = tagList(h1("Home"))), page(href = "/contact", ui = tagList(h1("Contact"))) )
Deploy that as it is; /contact will work under
https://connect.example.com/content/<guid>/contact.
You may still set basepath if you would rather not depend on that. What
Connect sends is not part of a documented, promised interface — it works, it is
tested, but Posit never committed to keeping it. Setting basepath costs you
nothing and pins the behaviour.
The detection only runs when the app really is on Connect. Behind your own
nginx, Apache or Traefik, basepath is not optional.
| What you see | What it means |
| --- | --- |
| The page loads but has no styling, and nothing is interactive | The css and javascript are 404ing. Set basepath. |
| A link takes you out of the app, or to a 404 | The links are missing the prefix. Set basepath. |
| One page works, a deeper one does not | Same cause, and deeper pages fail first. Set basepath. |
| Everything works but server_redirect() leaves the app | The redirect target is missing the prefix. Set basepath. |
| It works locally and fails deployed, with basepath set | Check the value: it is the prefix in the url, without the host. |
A single cause, a single fix. If setting basepath changes nothing, the value
is probably wrong: for https://example.com/team/myapp/, it is "team/myapp",
not "myapp".
Write internal links as absolute paths, the way you wrote your hrefs:
tags$a(href = "/contact", "Contact") # yes tags$a(href = "contact", "Contact") # depends on the current page tags$a(href = "../contact", "Contact") # depends on the current page
Only the first is rewritten with the prefix. The other two are resolved by the
browser against whatever page the visitor happens to be on, which gives a
different answer on / than on /team/profile.
A prefix is the kind of thing that passes every local test and fails the moment
it ships, because at 127.0.0.1:3000 there is no prefix to get wrong. If you
can, deploy once to a staging url with a prefix and click through the app.
You do not need this to use basepath, but it explains what you are looking at
in the page source.
On the way in, the prefix is removed from the requested path before brochure
matches it against your pages — so /myapp/contact becomes /contact. Some
proxies strip the prefix themselves before passing the request on, Connect
among them; brochure only removes it when it is actually there, so both kinds
of proxy work.
On the way out, every src and href in the response is made absolute:
href="/contact" becomes href="/myapp/contact". This also fixes something
that has nothing to do with proxies. Shiny declares its own dependencies with
relative urls, such as shiny-javascript-1.14.0/shiny.min.js. On a page like
/user/colin, a browser resolves that against /user/ and asks for a file
that is not there — which is why deeper pages break first. So the rewriting
happens on every response, and with no prefix it simply produces
/shiny-javascript-1.14.0/shiny.min.js.
Urls pointing at other hosts, protocol relative ones, fragments and query strings are left untouched.
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.