Jump to content

How do we... host nsane?


shought

Recommended Posts

nsane.down and nsane.forums, both individually, serve around 10.000 unique visitors who generate quite a few pageviews every day. At peak times we receive one or two pageview(s) every second, on average. 40 to 50 percent of these pageviews will originate from 'new' visitors, who do not have any of our files stored in their browser caches yet. One 'new' pageview will amount to anywhere between 15 and 75 requests to the server, depending on the page viewed. So the number of requests to each of our servers every second can easily exceed 150 at peak times.

Luckily modern webservers (in combination with sufficient 'horsepower') are equipped to handle this, but they don't 'just magically do'. Hence I decided to write this 'article' which will attempt to explain what is involved in providing you with... just a simple website. Feel free to skip any sections if you're already knowledgeable about some of the subjects, because I'm going to try and write this 'from the ground up' (the only prior knowledge I will assume is a superficial understanding of a browser and the Internet).

Note: a 'server' is both a 'computer' as well as 'an application which provides a service'. A webserver, database server and/or mail server could all run on one server (and in many situations do)

Table of contents

What is... a website? [↑]

A website consists of one or more webpages. Every webpage is written in HTML (HyperText Markup Language) and can include stylesheets (CSS: Cascading Style Sheets) as well as images, Javascript and much more. HTML determines the content and structure of a webpage; which information is included (title, content, images, CSS, Javasccript) and how certain content is 'separated' from other content (menu, sidebar, etc.). CSS provides a website with style; is the text black or blue, how wide are the menu buttons, etc. Javascript is a client-side scripting language which simply means that it is executed on your computer, not on the server. As such it has access to every part of the webpage, as displayed on your screen, and can perform manipulations on this content (change HTML or CSS, add an image or even write new Javascript).

Our setup [↑]

The following is a (simplified) graphical representation of our setup (don't worry, everything will be explained in the following paragraphs):
ServerLayout3.png

For now it will suffice to explain just the following parts:
Internet: that's you, all of you visiting our websites.
lighttpd: our webserver of choice which handles all incoming requests and either forwards them to PHP or directly replies to the request with a static file.
PHP FastCGI: PHP is a server-side scripting language which, together with PHP files (which store code/functions), is responsible for 'creating' the pages you're viewing. FastCGI indicates 'the way we run PHP', it will be discussed later on.
Percona (MySQL replacement): the database server which stores information such as accounts, posts and view counts.
Proxy server: question marks. Not because I don't know, but because the very nature of the proxy requires question marks (as in it will not be discussed here).

Dynamic versus static content [↑]

It is important to differentiate between dynamic and static content. Static content (generally) does not change, examples are images, CSS and Javascript files. When lighttpd receives a request for static content it will simply read from the disk (or from its own cache) and send the requested file.

Dynamic content can be generated specifically for you and at the moment you visit a page. When you attempt to view the index of the forums lighttpd will receive the request and because the file requested (index.php) is dynamic the request will be forwarded to PHP which will execute the script. The script (index.php) will check whether you are logged in and if so gather quite some information about your user account (when was your last visit, do you have new PMs, are there new notifications, are you even allowed to visit our forums?!). This information obviously can't be stored inside the file itself, because that would require the information for each and every user to be stored in the file and the file would grow huge, making the system incredibly slow.

Hence this information is stored in a database server, MySQL, where you aren't 'insert your name here', but you are a number (I'm sorry, but that's how it is!). So PHP will need to send a query (question) to MySQL. My number is 9745, when I visit the index this query (heavily simplified) will look something like this "SELECT `username`, `group`, `etc.` FROM `user_info` WHERE `user_id` = 9745;". The same happens for all the forums, topics, posts, etc.; they are all retrieved from the database. PHP will process this information and turn it into a webpage by pasting various bits of information together inside a certain 'template' (the visual appearance of the website, which includes references to all required images, stylesheets (CSS) and Javascript files) and forwards it back to lighttpd which will in turn forward it back to me, the visitor.

Caching, caching, caching! [↑]

Caching is the process of storing data in a location where it can be retrieved faster. In computing and on the Internet closer is usually faster, but there are also differences in storage: RAM (Random Access Memory) provides faster access to data than a hard drive (irregardless of whether this is a HDD or a SSD).

In the image above you might have already spotted two references to a cache; Memcached and ‘OPcache’. There are a couple more caches though; your very own browser has a cache for static content, lighttpd caches static content, Percona (MySQL) makes use of a query cache, the filesystem itself caches frequently accessed files. This many caches might seem redundant, but they all serve different purposes which I will shortly outline below (using a Javascript file (.js) as an example).

Browser cache - When your browser receives static content (for instance an image, CSS, Javascript) it will not only use the file on the current page but also keep a copy for later. Example: when you load the index a lot of static content is loaded, Javascript for instance, this content is exactly the same when you visit the New Content page, so there's no reason to ask the server for it again (within a short time frame).

lighttpd cache - When our server receives a request for a .js file it will read the file from the disk, compress it and then store it in memory. lighttpd is designed to simply store the compressed copy on the disk, but we've configured the server to store that particular directory in the memory, for faster access. So this wasn't really supposed to be a caching system, but we configured it to work like this.

Filesystem cache - When files are frequently accessed the filesystem itself will place them in memory, this isn't really relevant for us since the lighttpd cache already covers this.

OPCache - The OPCache caches compiled PHP files. It is an opcode cache which means it keeps a compiled copy of the bytecode of every PHP file (in memory), this removes the overhead of having to compile the code every time and reduces reads from the drive even further.

Memcached - Memcached is a general purpose (distributed) memory caching system. IPB makes use of Memcached to store data which is frequently used. Most of this data consists of (modified) MySQL results.

MySQL query cache - MySQL caches queries and their results in memory. If we were to run the same query twice in a row, the first one might take 100 ms and the second just 10 ms, because all MySQL needs to check is 'has this the data changed' and if the answer is no it can answer the query immediately, from memory.

Custom cache - the custom cache is only used by the frontpage and caches the (again: modified) MySQL results. For instance the whole index, all the topics in the latest community activity box and the news headers and the categories. It stores these results on the disk, but we've employed the same trick mentioned earlier, in the lighttpd cache, to store that portion of the disk in memory.

So I've just explained the possibility of using different forms of storage, but what about actually getting data closer? The browser cache achieves this, but it doesn't work for the first pageview. In order to get data physically closer to the end-user it's possible to employ a CDN (Content Delivery Network).

ServerLayout3.png

Knowledge of the Internet Protocol is necessary to understand how Cloudflare works, so I'll discuss it briefly below.

The Internet Protocol (IP) [↑]

As you may or may not know (almost) every network, and within those networks every system, has a unique IP address which is used to identify that particular system. When you visit nsaneforums.com the first thing your computer does is ask a DNS server (Domain Name System) 'Which IP address is associated with nsaneforums.com?'. The DNS server will reply with the appropriate IP address (let's say 9.9.9.9) and your browser, knowing that nsaneforums.com is actually 9.9.9.9 will now ask 9.9.9.9 for a webpage. Your router has no idea 'which way to go in order to find' 9.9.9.9 is, but your ISP's (Internet Service Provider) routers do and hence your request is forwarded to them and they will forward it to our server.

Cloudflare [↑]

Cloudflare is both a 'caching CDN' as well as a DDoS protection system (and much more, but since I'm not their marketing manager we're not going to discuss everything). In order to enable Cloudflare we have to point our domains (nsaneforums.com and nsanedown.com) to (one of) their IP address(es) instead of our server IP address. After that we provide them with our server IP address so they know where to forward the requests. When a request now reaches their servers they will forward it to our server, effectively 'hiding' our server (kind of like a proxy).

However Cloudflare has a whole network of servers in various locations and when someone in Russia connects to our website it is definitely not a good idea to have their request go to a datacenter in Australia and then back to the continent. Hence the IP addresses of Cloudflare do not point to one location. A visitor from China will reach the Hong Kong datacenter and a visitor from Australia the Sydney datacenter. This is effectively achieved with routing using the BGP (Border Gateway Protocol), which is something which will not be discussed in this description. For now it suffices for you to understand that different visitors connect to different servers.

This idea is essential to understanding how a CDN works: when someone requests content from our website (an image, for instance) Cloudflare will have a copy of that content in every datacenter, so it can immediately reply with the content (of course Cloudflare will have to verify, every once in a while, whether the content is still the same). Needless to say this works fine for static content, not so much for dynamic content (although it is possible to do this with dynamic content as well, but that, again, is an advanced topic).

Cloudflare also has the potential to protect our servers from DDoS attacks. But what is a DDoS attack? DDoS stands for distributed denial-of-service (attack). A normal DoS (denial-of-service) attack can be quite a few things, but the most common category of denial-of-service attacks (the only one which I will discuss) is flooding the server with traffic, rendering it (or the connection to the server) unable to produce any responses for other visitors. A distributed DoS attack then is an attack where not one, but multiple computers work together (usually in the form of a botnet) to flood a server with traffic. As you might understand a server won't easily be flooded by traffic from just one computer, but a few thousand (let alone a few million) will probably do the trick!

So how does Cloudflare help? Because it is very likely that the computers infected by a botnet are in numerous physical location all the traffic is spread out over all of Cloudflare's servers (so instead of a single server receiving 2.000 requests, there will be 20 servers receiving 100 requests). Now if a botnet is large enough (the largest known botnet that has existed is approximated at 30 million computers) this might still cause a problem, as 30 million divided by 20 is still well over a million requests to each server.

Cloudflare's 'servers' aren't just one server at each location though; each location has numerous servers which together handle the load of all those requests (I don't have exact numbers). But more importantly Cloudflare knows which computers/networks (recognized by their IP address) are involved in DDoS attacks simply because they receive these attacks all the time. So when someone launches a DDoS attack against nsaneforums.com most of the IPs will already be in Cloudflare's database and when their automated system recognizes that we are under attack it will simply block those requests. Cloudflare employs various mechanisms to separate the 'good' from the 'bad' traffic (and adding the IPs responsible for the bad traffic to their database), however I won't discuss these now.

So Cloudflare (1) protects us from attacks, (2) speeds up (static) content delivery for our visitors by bringing it closer and (3) helps in keeping some load of our servers by handling requests for static content without connecting to our servers. This seems too good to be true and quite frankly it is (general life lesson: if something seems too good to be true, it generally is). Cloudflare has some downsides such as the 'loss of autonomy' (if their servers go down, so will our website) and the delivery of dynamic content is slowed down somewhat (in the range of 1 to 10 percent), but weighing these small cons against the vast array of pros made the decision to stick with Cloudflare relatively easy.

PHP FastCGI [↑]

PHP FastCGI is a specific implementation of PHP where, instead of starting a PHP process every time a PHP file is executed, PHP is running continuously. There is one 'master' PHP process which provides several 'slave' PHP processes with instructions and a common cache (OPCache). The slave processes are responsible for executing the PHP code. In practice running PHP like this is much faster although average resource usage will most likely be higher than with a 'traditional' PHP implementation. Since RAM is cheap, performance is scarce and peak RAM utilization is really all that matters (as opposed to average) we've opted for performance.

Databases and storage engines [↑]

MySQL used to be the default database server for CentOS (our OS of choice), but it was replaced by MariaDB. We have opted to use Percona (another MySQL alternative) because of its excellent performance.MyISAM used to be the default MySQL storage engine but since MySQL 5.5 InnoDB took over this position. There are many differences between InnoDB and MyISAM but I will limit my discussion to the most interesting practical implication these differences have for us. MyISAM applies table-level locking whereas InnoDB supports row-level locking which enables more concurrent actions on a table. A table is where information is stored in an 'Excel like' fashion; every row contains one record and every column indicates a certain property of that record. Using MyISAM the entire table will be locked during the time something is being written to it, so all consecutive writes will have to wait. Since InnoDB supports row-level locking this isn't an issue.

Since we have many large tables which generate a lot of reads and writes it would be detrimental to performance to lock the entire table any time someone is writing to it. Do note: InnoDB requires more memory than MyISAM.

The servers [↑]

Our servers are virtual servers which means that one physical server is being shared by multiple users (which obviously don't have access to each others portion of the server). Due to security considerations we won't release any hardware specifications.

The servers reside in different datacenters and backups are synchronized to all servers. Furthermore the setups are standardized (the same operating system, the same servers/applications, the same versions).

PHP and Javascript: scripting languages [↑]

PHP is our server-side scripting language of choice whereas Javascript provides us with client-side scripting abilities. The distinction being that server-side scripts are executed on the server and client-side scripts on your PC and as such they have access to different features. Server-side scripting languages, such as PHP, can (directly) interact with the various servers running on the server (such as database, mail and/or search servers). Client-side scripts, Javascript, can interact with the webpage after it leaves the server and has reached your PC; it can manipulate HTML, at any time (changing how things look, or even the content).

Here are some (modified) scripts, with explanations, which are used to power nsane.down:
- PHP: Topic List
- Javascript: Tab Rotation

Note: all (custom) PHP functions have been renamed for security purposes.

Performance and website design [↑]

As explained at the start of this article a website consists of various components including images, stylesheets and Javascript (in addition to HTML). Many websites contain a large number of images, a couple of stylesheets and a few Javascript files and for each of those files a request has to be sent to the server. However transmitting 10 kB 5 times is slower than transmitting 50 kB in one round trip due to the costs of establishing a connection. Think of it as the difference between moving furniture using a minivan and a transport truck. Hence reducing the number of (HTTP) requests is interesting as it improves the user experience and decreases the server load at the same time, win-win. Yahoo! provides a great guide which covers the 'Best Practices for Speeding Up Your Website' and explains how to reduce the number of requests, amongst other things. It basically involves combining several files into one or a few and there are various techniques to do this. These techniques are covered in the guide by Yahoo!, so please have a look at it.

The custom cache and uptime [↑]

The custom cache helps take some of the load of the database by caching the news topics, the index (updates), the categories, the downloads index and even the request pages/listings (where you can download applications). The only things we don't cache yet are the 'news pages'/individual update topics (which appear on the index) and the comments on these topics. As you can see in the server layout the frontpage server has to connect to the forums server to retreive the aforementioned information and if the forums server would go down the frontpage server would also go down (because all of its requests for information would be left unanswered). At some point we realized that, since we already cached most information, we could start caching almost all information and if the forums server ever went down simply continue serving the information from the custom cache.

This required rewriting portions of the custom cache as well as the code used to query the database and turned out to be a little more difficult than anticipated. First of all simply serving the content from cache every time a request failed didn't work, because the server still made a request for information each time and had to wait for it to fail before it served from the cache (causing the requests to pile up just like they did before and effectively only delaying the server going down with a minute or so). Therefore we decided to disable querying the database for 30 seconds (by writing a value to the cache, which is checked before every query) if an error was found and this worked, the server would remain up even if the database was down. However I'm sure some of you will remember being presented with the nice red notice at the bottom ot the screen which says 'Some information on this page is served from the cache' quite frequently, this happened because we configured the 'query code' to start serving from the cache as soon as (just) one error occurred. Although this is perfect because it makes sure people will always be able to view our most essential pages it resulted in very frequent annoyances because the Internet and our server are simply not perfect. Requests, even though nothing is 'really wrong', will fail at times and cause the message to be displayed for 30 seconds, even though there wasn't really anything wrong.

So we decided to make another modification to the 'query code'. Although it is likely that a request will randomly fail it is much, much less likely that two requests fail. So when one request fails a value is written to the cache, indicating that one request failed, and the database isn't queried for 8 seconds (without any notice). If the next request is successful the value is simply discarded and the database will be queried again. However if the next request (the two requests will be about 8 seconds apart) fails as well the cached value is updated, the database will not be contacted for 32 seconds and a message is displayed indicating that the database is probably down. This is how we extended the functionality of our custom cache to also provide us with better uptime.

Closing remarks [↑]

As the title indicates this discussion only covers how nsane is hosted. I should mention that there are a vast array of possible approaches to hosting a website. There are various webservers, server-side scripting languages, database servers and a lot of 'miscellaneous tools' which can be involved in providing a website and the 'tools' we use are by no means the 'standard' (some, such as MySQL, might be, but others like lighttpd definitely aren't).

My intention was to provide a quick overview of the technical aspects of providing nsane.down and nsane.forums, but when I started doing this I realized that in order for people to understand I had to explain much more than I expected. During this 'quick' tour of our setup I've discussed HTML, the Internet Protocol and differences in storage engines for MySQL. I did not intend to mention any of these beforehand, but I realized most of this knowledge was essential to really understanding what is going on.

During the creation of this article I realized something else: our setup is actually more complicated than I thought. It's relatively easy for us to find out where a problem occurs and why it does when something is amiss, but the reason for that is that we've had to reinstall the servers on several occasions over the past few years. Anyhow I hope to have provided you with at least a basic understanding of how our setup works and I'll try to answer any questions if there are still some left. Also if you have any suggestions I'd be happy to hear them (in terms of improving this article, or in terms of our server setup).

Link to comment
Share on other sites


  • Replies 54
  • Views 6.3k
  • Created
  • Last Reply

Top Posters In This Topic

  • rudrax

    17

  • shought

    14

  • DKT27

    5

  • dcs18

    5

Top Posters In This Topic

Some possible critiques/questions I'd like to answer before anyone asks:

- Why not nginx (different webserver)?

First we used Apache, but it used to be a real memory hog. Hence we moved to lighttpd, maybe without properly evaluating the decision between nginx and lighttpd. If we had to pick now I think we'd go with nginx, but we don't want to 'fix what's not broken' ;)

Additional topics (which might be added into the 'article' later, if there are specific questions let me know!):

- Information about PHP, Javascript (with some examples of actual code);

- Analysis of performance improvements related to website design (such as combining requests for static resources into one);

- Discussion of our 'Custom Cache' with respect to the way in which it helps in providing better uptime, even when the database is down.

Link to comment
Share on other sites


Link to comment
Share on other sites


Thanks for all the info shought! :thumbsup:

Regarding Cloudflare, do you have control over it on how often they synchronize the entire site with their servers? Whenever the site is down and Cloudflare kicks in, they serve up around a week-old copy of the site. Do you have control over it and make it a little recent perhaps?

Link to comment
Share on other sites


Ck_kent is right. CloudFlare is useless. Whenever cloudflare tries to seduce us (when forums or FP goes down), it completely sucks. The live version of the site that it avails us, remains very old and you can't access anything except the home page. So, shought, if you guys are paying CloudFlare for keeping the Home page alive (they can't even show all the avatars), don't. Rather, we will kick your butts in the facebook page.

Link to comment
Share on other sites


We're using the free Cloudflare version and it most definitely isn't useless. You might recall that we were under some heavy DDoS attacks a while ago, Cloudflare has completely eradicated this problem, for free.

In addition to that if one of the servers is down Cloudflare will serve an old copy (and there's no way to change the frequency this copy is updated, besides paying them). I agree this is pretty much useless, but at the same time it doesn't hurt either (for otherwise the entire site would be down).

As described above (in the article) Cloudflare does much, much more than simply 'keep the site online when its actually down'. That's just one of its features and definitely not the reason why we started using them. The reason we started using them was the DDoS attacks and the reason we continued using their service was the fact that it also provided performance benefits.

Link to comment
Share on other sites


We're using the free Cloudflare version and it most definitely isn't useless. You might recall that we were under some heavy DDoS attacks a while ago, Cloudflare has completely eradicated this problem, for free.

In addition to that if one of the servers is down Cloudflare will serve an old copy (and there's no way to change the frequency this copy is updated, besides paying them). I agree this is pretty much useless, but at the same time it doesn't hurt either (for otherwise the entire site would be down).

As described above (in the article) Cloudflare does much, much more than simply 'keep the site online when its actually down'. That's just one of its features and definitely not the reason why we started using them. The reason we started using them was the DDoS attacks and the reason we continued using their service was the fact that it also provided performance benefits.

If it's free then it's fine :)

Link to comment
Share on other sites


Added information about 'The custom cache and uptime' and 'Website design and performance' ;)

Link to comment
Share on other sites


Added information about 'The custom cache and uptime' and 'Website design and performance' ;)

Thank you !!! :notworthy:

Link to comment
Share on other sites


  • 2 weeks later...

Added information about 'PHP and Javascript: scripting languages'.

(With actual examples of code used on nsane.down.)

Link to comment
Share on other sites


  • 2 weeks later...

Dear shought,

I'd like to know about the speed that FP has acquired. It loads amazingly fast. I've never seen a page loading that fast in my 56K dial-up connection.

Link to comment
Share on other sites


I'll probably add some more details to this post later, but for now I'll give a short summary of the practices we employ to speed up our website. There are several factors which determine the (perceived) speed of a website.

(1) the distance to the server (also known as ping time), (2) the 'speed' (bandwidth) of the connection to the server, (3) the time it takes the server to generate the page (access the database, perform manipulations on the data, etc.), (4) the amount of resources included on the page (every resource requires another request and as such another round trip to the server), (5) the size of the resources on the page (a larger resource takes more time to download than a smaller one) and (6) the order in which the resources are loaded.

To improve (1) we have servers in an area with great internet connectivity (which reduces ping times) and employ Cloudflare, a caching CDN, which caches our static content on their global network. This means that a request for static content will have to travel a shorter distance (and time) than one for dynamic content (please refer to the article above for the difference between static and dynamic content).

(2) can be compared to amount of lanes on a highway. Whereas the distance between point A and B determines the time it takes to reach that point the amount of lanes determines how many cars (how much data) can travel at the same time. The terms used to describe this are uplink, port speed and bandwidth, but bandwidth is also frequently used to indicate the data limit in a given month, so to avoid confusion most providers use port speed or uplink. We've ensured that all our servers have a 1 Gbps uplink to make sure we can provide even the most demanding downloads.

To save some time on (3) we optimize our website code and employ a lot of caching mechanisms (please refer to the above article, chapter 'Caching, caching, caching', for the specifics). Optimized code is great but only shaves off a couple of milliseconds whereas caching really makes a huge difference.

In (4) we can reduce the number of resources using various techniques ranging from image maps to the combination of various Javascript and CSS files into single files. (Again: please refer to the section about 'Website design and performance' for more specifics.)

Optimizing (5) involves reducing the file size of resources, this is most commonly achieved by compressing content with gzip and applying advanced PNG compression algorithms for images/logos. However you could also shave some bytes off the HTML by having a simple website design. In addition to that design decisions also matter; we could have easily included a Facebook Like-button, Twitter-button and various other buttons separately, but instead we used AddThis which includes a ton of social media sites at the cost of just a small Javascript snippet, which is much smaller than just the Like-button alone.

(6) is a bit tricky, but has rendered some nice benefits especially for those of you on slow connections. This step requires the realization that some resources are more important than others; HTML, CSS and images are required to display the page whereas Javascript provides the 'dynamic nature' of the site. So we load the HTML first, then the CSS, then the images, then the functional Javacript and then we call a Javascript function which includes the share buttons, the ads and our statistics code. Have you ever noticed the ads popping up after the page finished loading? This is why.

Additional note for (5): we also have a collapsed index which does not load the software logos. The logos are post-loaded when they are required and this decreases the size of our index by 50%, which is especially helpful for mobile (G/3G) usage which is still often relatively slow.

Link to comment
Share on other sites


Have you ever noticed the ads popping up after the page finished loading? This is why.

Interesting.

Does the forums' server follows the same sequence, i.e. ads load in the last?

Link to comment
Share on other sites


Have you ever noticed the ads popping up after the page finished loading? This is why.

Interesting.

Does the forums' server follows the same sequence, i.e. ads load in the last?

Yep.
Link to comment
Share on other sites


Have you ever noticed the ads popping up after the page finished loading? This is why.

Interesting.

Does the forums' server follows the same sequence, i.e. ads load in the last?

Yep.

Then why clicking on notification icon before loading the ads redirects me to notification page instead of popping the notification javascript up? Is it because my connection is slow?

Link to comment
Share on other sites


Interesting.

Does the forums' server follows the same sequence, i.e. ads load in the last?

What ads. :dunno:

infolinks.

Link to comment
Share on other sites


Interesting.

Does the forums' server follows the same sequence, i.e. ads load in the last?

What ads. :dunno:

infolinks.

Never heard of 'em - maybe screenshots would help. :unsure:

Link to comment
Share on other sites


Screenshot won't be possible for that because my hosts file is severely edited.

Link to comment
Share on other sites


Screenshot won't be possible for that because my hosts file is severely edited.

:hehe:

If I upload the screenshot with red line, shought will kick my butts right away.

Link to comment
Share on other sites


  • Administrator

We don't force users to allow ads, but we highly expect our ViP and Veteran members to allow/respect them in order to allow the site to function with it's needful financial requirements.

To add to shought's post, about or more than 50% of the software logos on frontpage are optimised with high compression png compression tools, which compress the png files by almost 50%, with the quality loss of 0.01% or less. :)

Link to comment
Share on other sites


We don't force users to allow ads, but we highly expect our ViP and Veteran members to allow/respect them in order to allow the site to function with it's needful financial requirements.

To add to shought's post, about or more than 50% of the software logos on frontpage are optimised with high compression png compression tools, which compress the png files by almost 50%, with the quality loss of 0.01% or less. :)

Tell me then which link I have to remove from my hosts file?

Link to comment
Share on other sites


Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...