Show HN: I created a URL shortener that can be entirely hosted on GitHub Pages
github.com
github.com
it does need a database though. it just uses someone else's database
This is terrible and so computationally wasteful. But it's interesting to think about.
A well-known example of this is bogosort: https://en.wikipedia.org/wiki/Bogosort
In the Turing machine model of computation, algorithms that use O(1) space are theoretically equivalent to finite state machines (potentially with a very large number of states). Logarithmic space is where things start to get interesting, because it's enough to store a constant number of pointers into an arbitrarily large input.
Also, conceptually, it seems like you're not so much using pi as a database as you are using it as an encoding mechanism: turning "abc.com" into a string of numbers whose length is relative to the original string. At that point you might as well pick an easier encoding, like base64. :)
Thus, I wrote this URL shortener which does not need a backend to work at all. It uses GitHub issues as a "database", and unlike most other URL shorteners that do not need backends, this one doesn't need a # prefixed to the alias. (i.e. short URLs look clean like this: nlsn.cf/1 instead of looking like this: nlsn.cf/#1)
I haven't had the time to work on it more but do let me know what you guys think! Thank you and hope yall enjoy it!
You might want to detect infinite loops as well :) https://nlsn.cf/6
Last note, the status code seems to be incorrectly set to 404 instead of any of the redirection status code. If you're hosting it on GitHub pages, I'm not sure you can actually control it. If the status code was correctly set + the Location header, I think all browsers would be able to detect the infinite loop automatically and prevent it.
By the way, your feedback is good for author improves. Thank you.
Yep I actually cannot control the status code at all. Part of what made this a fun and hacky project was the fact that I was actually redirecting to the actual URLs by catching the 404s. So, the only way i could think of catching the infinite loop was to supply the domain. That being said, I do appreciate the feedback and it's always great to learn more!
I think you could catch the infinitive loop client-side as well by comparing what the URL to redirect to is, and comparing it with the current URL. If they are the same, prevent the redirect. Although you would still be able to create loops by using two issues instead. Maybe just comparing the full hostname? If it's nlsn.cf, don't redirect.
Doesn't work; eg, make http://someothershorten.er/foo -> http://nlsn.cf/1234 -> http://someothershorten.er/foo.
GitHub issues infrastructure is a backend!
I felt exactly the same way and did, in fact, build one.[1]
It was pretty simple. There's an interesting twist to the particular one I built - it's not specifically a URL shortener, it's a "Universal Shortener".[2] Also no tracking or cookies or third party connections, which I find pleasing.
The disappointing part is seeing the malicious spam and phishing URLs that get shortened. We have had to manually flag-hold-approve a lot of strings and IIRC we outright block anything with 'paypal' in it.
We also gave a kill-function to our upstream ISP so that they could immediately act on phishing complaints without our having to be involved in real-time.
[1] https://0x.co
I also sent a notice to all rsync.net customers, years ago, when we launched it.
That's it.
I've spent almost zero time on "Oh By" in the last 24 months as things at rsync.net have been very busy. So, we haven't made a sale in months due to lack of publicizing it.
However, I think there are unknown, emergent use-cases out there for "Oh By" that I hope people will discover. I am going to pivot back to "Oh By" next year ...
Then google can just find that index page and index every link on it - and all codes marked searchable will be indexed by google.
<meta http-equiv="refresh" content="1; url='https://news.ycombinator.com/'" />
in the header.
GitHub does have a 60 calls per hour API limit so I don't think it affects them as much(?) That being said this project is meant to be for fun and definitely not recommended for production purposes HAHA
It is an artistic and creative way to shorten urls, but likely won’t exist forever. As long as it does though, we can appreciate the art for what it is.
I wonder if anyone has made this observation before in regards to such things: "Anything that can be used to store arbitrary data, will be used to store arbitrary data."
Like licensing arguments?
It uses a tab-delimited nginx map file as a config. To make changes, I wrote a tiny shell wrapper which opens the config in Vim, checks it in, and reloads nginx: https://statico.link/how
People frequently asked me for a list of all of the links, so I wrote a tiny server to serve them: https://github.com/statico/statico-link-list
Babel though? Packages? JavaScript? This could just be a single HTML file, surely?
https://github.com/nelsontky/gh-pages-url-shortener/blob/mai...
First I'm always fascinated by smart solutions that make use of existing free (as in beer) infrastructures to provide a service with open-source software. In this way we truly get FOSS.
Second, I really like the idea that the information on where the shortened url redirects to is publicly available. I know that solutions like bitly.com do provide a way to preview the shortend url, but I think this is just more transparent, although admittedly a little bit less obvious.
In what way will this lead to `truly` FOSS? Sure, the URL shortener project is open-source, but neither Github Pages nor Github Issues are free-as-in-libre open-source projects. What is being show-cased here is a neat idea, but ultimately the dependency on Github's Pages and Issues will make it difficult to port to other systems, thus you are not really free to do with as you please.
So, while the URL shortener program itself is FOSS with all the right licenses, doesn't the direction you are proposing lead to more capture by these non-free platforms, as the value they offer for free-as-in-beer becomes more and more difficult to ignore?
My thinking is you could store the links and the corresponding shortened URL lookups as an element in the HTML source itself. You would have a submit input on the page. User enters the url they'd like to shorten (or is pulled from a param). The JS reads that value and then looks it up to see if it already exists in the element you created for storage. If not, it creates a hash as the shortened 'link' and adds it to a hashmap / element. Now this is the crucial part: have JS call the Github Actions API to trigger a workflow (https://docs.github.com/en/free-pro-team@latest/rest/referen...) that puts the updated hashmap (which is a payload for a custom parameter you defined for the GH Action) into a file e.g. `links.json` or even outputted directly into the HTML source. Then have the action commit this file back into the repo. Thus, the flat file in the repo acts as the storage mechanism for the links. From a UX standpoint, this is a little smoother and more cohesive (same site to input & redirect) than having to open a GH issue.
With this approach, you can still have the nlsn.cf short domain redirect as long as you pass the URL params and get them in JS from the location, which it looks like you already do. I might try whipping up a version of this tonight. Thanks for the inspiration!
I just found a "bug" that can be abused (in chrome at very least).
URL('http:53') returns https://0.0.0.53 (it casts the int32 number to an ip address, dont' ask why). But window.location.replace behaves differently and treats it as a relative URL.
Oh, wait.
But this has a chance of not working since you are doing the redirection client-side. Some addons block 3rd party resources. Looks like you're using the github API and sending the request from the user's browser.
I helped someone design a regular URL shortener in PHP before, they're not complicated to implement with a simple MySQL (or even SQLite) database and any language you're familiar with. I have not taken a stab at writing a client-side shortener yet on the other hand.
Edit: clarifying
If you want your own private shortener, I have a better idea:
Use GitHub Pages and create a `/1/index.html` (where 1 is the short link name) and add a “refresh” meta tag in it. Completely static and no build/JS necessary.
This could be opened up to others by combining it with issues and GitHub Actions, but who wants that?
Another better idea for a private URL shortener is hardcoding a list of URLs in a CloudFlare Worker. Much faster and with real 301 redirects.
OP, how does this get around CORS?
The network tab of developer tools should reveal all of this.
It should, in the right circumstances. By default, CORS dictates that cross-origin requests should not be allowed.
But sometimes you want that to be possible, so we have headers available to is where we can signal which domains are allowed to access the origin when on another origin.
In the case of the GitHub API, they (GitHub) are setting these headers to allow any origin to access the GitHub API from any other origin, that's why your browser doesn't throw a security error. Check out the various "access-control-*" headers the GitHub API returns as response headers when you use it.
It has nothing to do with custom domains or GitHub Pages, but all to do with CORS and associated headers. You can learn more about CORS here: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
I know perfectly fine what you mean here, but in the name of security, it's important to be precise.
All websites and browsers have CORS, one way or another, as CORS is the general concept. By default, only "same-origin" requests are allowed and "cross-origin" requests are disabled. But CORS is still there none the less.
What GitHub has done in this case, is add support for "cross-origin" requests.
Nitpicky maybe, but thought it'd be useful to add to avoid any confusion.
Access-Control-Allow-Origin: star
I had this same ~dream of hosting a URL Shortener statically, which lead me to create URLZap. The difference is that it relies on a config file and it can be used on any code repository service https://github.com/brunoluiz/urlzap
Shameless plug for a similar project that i did few years back: A (partial) static site generator based on github issues :) https://github.com/geekodour/gitpushblog
Then I got a little offended by myself. Information Theory has stated clearly that universal compression is impossible, and how come that I didn't recall that?
What if some of shortener domain so popular that Github takes some action? Like block or ban?
Hopefully it wouldn’t be considered abuse just by its nature (as opposed to any traffic spikes it might cause). Quite the reverse, this shows off the flexibility of the service!
> Unlike many URL shorteners, this one does not need a database and can be entirely hosted on GitHub pages.
Interesting take!
EDIT (After 1 minute): Looks like its working again. Wonder how such a service can go down. Was Github's server failing or DNS?
Yes, this is an interesting technical demonstration of a concept, but it also has some rather serious problems, so that I would strongly recommend that no one actually do things this way.
----
Firstly, the size of the code: I just golfed it for fun, I think this should do for the full document (though I haven’t tested it):
<!doctype html><meta charset=utf-8><title>Redirecting...</title><script>e=new XMLHttpRequest(),e.addEventListener("load",function(){try{if(/^https?:\/\/(?!nlsn\.cf(\/|$))/.test(e=JSON.parse(e.responseText).title))return location.replace(e)}catch(e){}location.replace("/")}),e.open("GET","https://api.github.com/repos/nelsontky/gh-pages-url-shortener-db/issues/"+location.pathname.split("/")[1]),e.send()</script>
100KB in four requests, down to 412 bytes in one request (and that 20 bytes of <meta charset=utf-8> isn’t even particularly valuable). And it restores support for IE, by using simple regular expression testing instead of `new URL(…)`, which IE never supported. As written, I believe it should work down to IE9, and older could be supported if you replaced the load event handler with an onreadystatechange incantation.(Other general remarks on the HTML:
1. In <meta charset="utf-8" />, the trailing slash is completely ignored by the HTML parser; in fact, I reckon using trailing slashes like this is actively slightly harmful, because it misleads you into thinking it closes elements, as in XML, but it doesn’t.
2. On the script elements, type="text/javascript" is the default and thus not needed.
3. <html>, <head> and <body> wrappings can all be omitted from the source. This one is a bit more subject to taste; I know some like their source to match the parsed document tree, even down to things like writing the <tbody> out even if there is no thead or tfoot. But I myself always omit the end tags on these, and omit the start tags unless they have attributes, which in practice means I always have the <html> for its lang attribute.
)
----
Secondly, the redirection technique. There are good reasons why you should use HTTP redirects and not JavaScript location.replace for something like this:
1. If you use JavaScript, any users unable to run the JavaScript are left high and dry. This includes people like me that disable JavaScript by default (I because it makes the web so much faster and less annoying; others for privacy reasons and similar), but it would also help people on poor-quality connections, especially if the JavaScript is loaded in a different connection (which will probably not be the case here), because it’s surprisingly common for parts of pages’ resources to simply fail to load sometimes on poor-quality connections.
2. Various tooling like link checkers likewise won’t be able to work with this. As it stands, even link checkers that ran JavaScript would fail on this because you’re also using location.replace to show the “bad link” error. (Another of my pet peeves: doing a redirect to a “not found” page, rather than serving the “not found” page at the original URL with status code 404.)
3. It’s perfectly feasible for the connection to https://nlsn.cf to have succeeded, but the connection to https://api.github.com to fail. When this happens, you’re left trying to decide what to do; and location.replace("/") is a very bad solution, because you’ve now trashed the URL and I can’t even just try reloading the page to see if it works second time round. If you use HTTP redirects, everything will work perfectly in all cases, regardless of which connections succeed or fail.
4. Browsers have redirect loop detection, but you’ve opted out of that. In this thread someone pointed out simple loops and you’ve now tried to work around this by telling it “don’t redirect to the same domain”, but this really isn’t enough: you can easily have A → B → A. If this was done with HTTP redirection, the browser would twig and show an error and stop; but because half of the loop is done with location.replace, this won’t be caught. (Also, there are perfectly legitimate cases for a short URL redirecting to another short URL which redirects to a long URL, which this has now broken.)
----
A more amusing concept that occurs to me is implementing the URL shortener in a service worker, so that you can issue proper HTTP redirects, while doing it only on the client side.
1. I looked at https://github.com/nelsontky/gh-pages-url-shortener/blob/mai... and it is a basic script that does nothing more than redirection. A lot of URL shorteners on the web do a lot more. Premium ones can track users and route links based on client.
2. Most of URL shortening audience is non-technical and they usually like to pay and forget about it. Hosting and running your own service is an overhead.
3. Handling traffic at scale is challenging. So, if you have a lot of hits, I would not advise spending time maintaining the service.
4. Github can kill this anytime.
That said, this is neat solution for basic needs.
And target enterprises with custom URL's. They look very good
Maybe copy some features adjust has (deep linking and redirecting to respective app stores with tracking parameters)
Businesses that I target have set up custom domains and reap more benefit from it.
I eventually plan to retire default shortening domain(blanq.io) and only offer custom domains.
2. who "likes to pay"? Having a free option is great, no matter whether you're "non-technical" or not...
3. It's hosted on GH Pages. I think they're pretty okay on the scaling front, what do you think?
4. Technically yes, as in "they could switch off GH Pages and the API". Not sure how likely that is. Also, the redirections are saved as GH issues
2. Businesses like to pay.
3. Github has been bad lately https://www.theregister.com/2020/07/13/github_takes_some_dow...
Examples are youtu.be , goo.gl , redd.it , cbsn.ws